# set the root to the project directory, not the rmd directory
knitr::opts_knit$set(root.dir = rprojroot::find_rstudio_root_file())
# default to not printing the code
knitr::opts_chunk$set(echo = FALSE)

# libraries
library(tidyverse)
package 㤼㸱tidyverse㤼㸲 was built under R version 4.0.5Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
-- Attaching packages ------------------------------------------------------------ tidyverse 1.3.1 --
v ggplot2 3.3.3     v purrr   0.3.4
v tibble  3.1.2     v dplyr   1.0.6
v tidyr   1.1.3     v stringr 1.4.0
v readr   1.4.0     v forcats 0.5.1
package 㤼㸱ggplot2㤼㸲 was built under R version 4.0.3package 㤼㸱tibble㤼㸲 was built under R version 4.0.5package 㤼㸱tidyr㤼㸲 was built under R version 4.0.5package 㤼㸱readr㤼㸲 was built under R version 4.0.3package 㤼㸱dplyr㤼㸲 was built under R version 4.0.5package 㤼㸱forcats㤼㸲 was built under R version 4.0.3-- Conflicts --------------------------------------------------------------- tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
library(sf)
Linking to GEOS 3.9.0, GDAL 3.2.1, PROJ 7.2.1
library(stars)
package 㤼㸱stars㤼㸲 was built under R version 4.0.5Loading required package: abind
library(tmap)
package 㤼㸱tmap㤼㸲 was built under R version 4.0.5Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
library(transformr) # not needed for this doc at present?
package 㤼㸱transformr㤼㸲 was built under R version 4.0.5
Attaching package: 㤼㸱transformr㤼㸲

The following object is masked from 㤼㸱package:sf㤼㸲:

    st_normalize
library(gganimate) # not needed here at present
package 㤼㸱gganimate㤼㸲 was built under R version 4.0.5
library(viridis)
package 㤼㸱viridis㤼㸲 was built under R version 4.0.5Loading required package: viridisLite
package 㤼㸱viridisLite㤼㸲 was built under R version 4.0.5
library(colorspace)
package 㤼㸱colorspace㤼㸲 was built under R version 4.0.5
# source in the data management

# source('directorySet.R')
source(file.path('Scripts', 'Scenarios', 'plotting', 'metabolismLocalAndStatic.R'))
          sysname           release           version          nodename           machine 
        "Windows"          "10 x64"     "build 19042" "DESKTOP-NU5UDHD"          "x86-64" 
            login              user    effective_user 
          "Galen"           "Galen"           "Galen" 

Purpose here is primarily to go through some plots, look at them, and make static versions for the paper. This is mostly a copy-paste and translate over from metabolismLocalAndStatic. I’ve done this to split out the data read-in, function definitions, and plotting (and particularly the plot trying-out). I’ve been using these notebooks for plot testing, because it’s really nice to be able to scroll around and see what we’ve done, rather than have to keep re-making plot objects when we forget what they look like.

The catch with this approach is the quick and dirty prototyping isn’t quite so quick, and this document ends up taking FOREVER to load. Might switch back? But it sure is nice to actually see what each figure I made looks like, especially when we’re likely to drop this for extended periods.

Set the date for examples

availDays <- st_get_dimension_values(weraiCropTemp, which = 'time')
datewanted <- as.character(availDays[17])

Static versions with drivers and outcomes

Make a static version using the tmap functions

suppressMessages(tmap_mode('plot'))

allfun(tempObj = weraiCropTemp, tempAtt = 1,
                   inunObj = weraiCropInun, inunAtt = 1,
                   gppObj = weraiCropPredGPP, gppAtt = 1,
                   erObj = weraiCropPredER, erAtt = 1,
                   datewanted)

Clean that up just a bit, with a grey background. probably pull the title off but it’s nice to have as reference while i’m making these

tm_grid_static <- tmap_arrange(tempfun(weraiCropTemp, 1, datewanted) +
                                 tm_layout(bg.color = "grey85"),
                               inunfun(weraiCropInun, 1, datewanted, titled = FALSE) +
                                 tm_layout(bg.color = "grey85"),
                               gppfun(weraiCropPredGPP, 1, datewanted, titled = FALSE) +
                                 tm_layout(bg.color = "grey85"),
                               erfun(weraiCropPredER, 1, datewanted, titled = FALSE) +
                                 tm_layout(bg.color = "grey85"))
tm_grid_static

I think for completeness let’s make a ggplot version too and see which is better. Takes more work, but have more control (probably just because I speak ggplot)

gg_grid_static <- ggpubr::ggarrange(
  tempfun(weraiCropTemp, 1, datewanted, plotPkg = 'ggplot') + 
    guides(fill = guide_legend(title.position = 'top')) +
    theme_grey(base_size = 8) + 
    theme(legend.position = 'bottom', 
          legend.background = element_blank(),
          legend.key.size = unit(0.3, 'cm')),
  inunfun(weraiCropInun, 1, datewanted, titled = FALSE, plotPkg = 'ggplot') + 
    guides(fill = guide_legend(title.position = 'top')) +
    theme_grey(base_size = 8) + 
    theme(legend.position = 'bottom', 
          legend.background = element_blank(),
          legend.key.size = unit(0.3, 'cm')),
  gppfun(weraiCropPredGPP, 1, datewanted, titled = FALSE, plotPkg = 'ggplot') + 
    guides(fill = guide_legend(title.position = 'top')) +
    theme_grey(base_size = 8) + 
    theme(legend.position = 'bottom', 
          legend.background = element_blank(),
          legend.key.size = unit(0.3, 'cm')),
  erfun(weraiCropPredER, 1, datewanted, titled = FALSE, plotPkg = 'ggplot') + 
    guides(fill = guide_legend(title.position = 'top')) +
    theme_grey(base_size = 8) + 
    theme(legend.position = 'bottom', 
          legend.background = element_blank(),
          legend.key.size = unit(0.3, 'cm')),
  ncol = 2, nrow = 2)
print(gg_grid_static)

Code block to print and make those into figs

pdf(file.path(scriptOut, 'Werai_tm.pdf'), 
    onefile = FALSE, height = 12/2.54, width = 16/2.54, useDingbats = FALSE)
print(tm_grid_static)
dev.off()
null device 
          1 
png(file.path(scriptOut, 'Werai_tm.png'), 
    height = 12/2.54, width = 16/2.54, units = 'in', res = 300)
print(tm_grid_static)
dev.off()
null device 
          1 
# Can I print those?
pdf(file.path(scriptOut, 'Werai_gg.pdf'), 
    onefile = FALSE, height = 12/2.54, width = 16/2.54, useDingbats = FALSE)
print(gg_grid_static)
dev.off()
null device 
          1 
png(file.path(scriptOut, 'Werai_gg.png'), 
    height = 12/2.54, width = 16/2.54, units = 'in', res = 300)
print(gg_grid_static)
dev.off()
null device 
          1 

Uncertainty

I think now that I have the functions, there’s a slicker way to do this, but I don’t want to reinvent the wheel at this point. Might want to turn this into a function?

ER

# ER
er_sf <- weraiCropPredER[1,,] %>% 
  st_as_sf() %>% 
  select(all_of(datewanted)) %>%
  rename(ER = 1) %>%
  mutate(logER = log10(1+ER), type = 'estimate')
er_sfPU <- weraiCropPredER[2,,] %>% 
  st_as_sf() %>% 
  select(all_of(datewanted)) %>%
  rename(ER = 1) %>%
  mutate(logER = log10(1+ER), type = 'upper')
er_sfPL <- weraiCropPredER[3,,] %>% 
  st_as_sf() %>% 
  select(all_of(datewanted)) %>%
  rename(ER = 1) %>%
  mutate(logER = log10(1+ER), type = 'lower')

er_sf_LMU <- bind_rows(er_sf, er_sfPU, er_sfPL) %>%
  mutate(namedPI = ifelse(type == 'lower', "Lower 95% PI",
                          ifelse(type == 'estimate', "Estimate",
                                 ifelse(type == 'upper', "Upper 95% PI", 
                                        'SCREWUP'))))

erlabel <- 'ER (kg 02/day)\nat max extent'

# Can get away with this because pretty ranges work out OK
erControl <- ersetup(data = weraiCropPredER, attnum = 1)

  er_gg_uncertain <- ggplot() +
    geom_sf(data = er_sf_LMU, mapping = aes(fill = logER), color = NA) +
    # geom_sf_label(data = ltimNoNorth, mapping = aes(label = ValleyName)) +
    coord_sf() +
    # Closest to the tmap
    scale_fill_stepsn(colors = erControl$erpal,
                      breaks = erControl$erbreaks[2:length(erControl$erbreaks)],
                      limits = c(min(erControl$erbreaks), max(erControl$erbreaks)),
                      labels = erControl$erlabels,
                      guide = 'legend',
                      name = erlabel) +
    facet_grid(fct_reorder(namedPI, .x = logER, .fun = max)~.) # lower, estimat and upper should always fall in that order for their maxes

er_gg_uncertain

GPP

# GPP
# I'm sure thgppe's a slick way to select all three attributes and do this in one
# go, but I just need to get this done
gpp_sf <- weraiCropPredGPP[1,,] %>% 
  st_as_sf() %>% 
  select(all_of(datewanted)) %>%
  rename(GPP = 1) %>%
  mutate(logGPP = log10(1+GPP), type = 'estimate')
gpp_sfPU <- weraiCropPredGPP[2,,] %>% 
  st_as_sf() %>% 
  select(all_of(datewanted)) %>%
  rename(GPP = 1) %>%
  mutate(logGPP = log10(1+GPP), type = 'upper')
gpp_sfPL <- weraiCropPredGPP[3,,] %>% 
  st_as_sf() %>% 
  select(all_of(datewanted)) %>%
  rename(GPP = 1) %>%
  mutate(logGPP = log10(1+GPP), type = 'lower')

gpp_sf_LMU <- bind_rows(gpp_sf, gpp_sfPU, gpp_sfPL) %>%
  mutate(namedPI = ifelse(type == 'lower', "Lower 95% PI",
                          ifelse(type == 'estimate', "Estimate",
                                 ifelse(type == 'upper', "Upper 95% PI", 
                                        'SCREWUP'))))

gpplabel <- 'GPP (kg 02/day)\nat max extent'

gppControl <- gppsetup(weraiCropPredGPP, attnum  = 1)

gpp_gg_uncertain <- ggplot() +
  geom_sf(data = gpp_sf_LMU, mapping = aes(fill = logGPP), color = NA) +
  # geom_sf_label(data = ltimNoNorth, mapping = aes(label = ValleyName)) +
  coord_sf() +
  # Closest to the tmap
  scale_fill_stepsn(colors = gppControl$gpppal,
                    breaks = gppControl$gppbreaks[2:length(gppControl$gppbreaks)],
                    limits = c(min(gppControl$gppbreaks), max(gppControl$gppbreaks)),
                    labels = gppControl$gpplabels,
                    guide = 'legend',
                    name = gpplabel) +
  facet_grid(fct_reorder(namedPI, .x = logGPP, .fun = max)~.) # lower, estimat and uppgpp should always fall in that ordgpp for their maxes

gpp_gg_uncertain

Combine the ER and GPP and save as figures

both_uncertain <- ggpubr::ggarrange(gpp_gg_uncertain + 
                                   guides(fill = guide_legend(title.position = 'top')) +
                                   theme_grey(base_size = 8) + 
                                   theme(legend.position = 'bottom', 
                                         legend.background = element_blank(),
                                         legend.key.size = unit(0.3, 'cm')), 
                                 er_gg_uncertain + 
                                   guides(fill = guide_legend(title.position = 'top')) +
                                   theme_grey(base_size = 8) + 
                                   theme(legend.position = 'bottom', 
                                         legend.background = element_blank(),
                                         legend.key.size = unit(0.3, 'cm')),
                                 ncol = 2, nrow = 1)
both_uncertain

# Just print the ggplots
# Can I print those?
pdf(file.path(scriptOut, 'Werai_uncertainty.pdf'), 
    onefile = FALSE, height = 12/2.54, width = 16/2.54, useDingbats = FALSE)
print(both_uncertain)
dev.off()
png 
  2 
png(file.path(scriptOut, 'Werai_uncertainty.png'), 
    height = 12/2.54, width = 16/2.54, units = 'in', res = 300)
print(both_uncertain)
dev.off()
png 
  2 

Bar charts

make a wide version of the data so I can have mins and maxes OR, should I do all of them and fct_order them?

Joining seems safe, but geographic joins are a mess. It doesn’t work at all. binding cols is actually safer and works

GPP

gpp_sf_LMU_wide <- bind_cols(gpp_sf[,-4], 
                           st_drop_geometry(rename(gpp_sfPU[,-c(4)], 
                                  UGPP = GPP, 
                                  upper_logGPP = logGPP)),
                           st_drop_geometry(rename(gpp_sfPL[,-4], 
                                  LGPP = GPP,
                                  lower_logGPP = logGPP))) %>%
  mutate(wetlandID = row_number())

Maybe for a subset of wetlands?

# Grab some set of wetlands
which(gpp_sf$geometry == gpp_sf_LMU$geometry[3])
[1] 3
exampleWets <- c(1:100)
bargpp <- ggplot(gpp_sf_LMU_wide[exampleWets, ], 
                 aes(x = wetlandID, y = logGPP, fill = logGPP)) +
  geom_col() + # the geom_bar equivalent when it's not counting cases.
  geom_errorbar(mapping = aes(ymin = lower_logGPP, ymax = upper_logGPP)) +
  scale_fill_stepsn(colors = gppControl$gpppal,
                    breaks = gppControl$gppbreaks[2:length(gppControl$gppbreaks)],
                    limits = c(min(gppControl$gppbreaks), max(gppControl$gppbreaks)),
                    labels = gppControl$gpplabels,
                    guide = 'legend',
                    name = gpplabel) +
  scale_y_continuous(breaks = gppControl$gppbreaks[2:length(gppControl$gppbreaks)],
                     labels = round(10^(gppControl$gppbreaks[2:length(gppControl$gppbreaks)]))) +
  labs(x = 'Wetland', y = gpplabel)
bargpp

I assume it is a mes to just throw them all on actually, with a fair amount of monkeying with the look, that’s OK

bargppall <- ggplot(gpp_sf_LMU_wide, 
                 aes(x = fct_reorder(as.factor(wetlandID), logGPP, .desc = TRUE), y = logGPP, fill = logGPP)) +
  geom_col() + # the geom_bar equivalent when it's not counting cases.
  geom_linerange(mapping = aes(ymin = lower_logGPP, ymax = upper_logGPP),
                 color = 'grey50', alpha = 0.2) +
  scale_fill_stepsn(colors = gppControl$gpppal,
                    breaks = gppControl$gppbreaks[2:length(gppControl$gppbreaks)],
                    limits = c(min(gppControl$gppbreaks), max(gppControl$gppbreaks)),
                    labels = gppControl$gpplabels,
                    guide = 'legend',
                    name = gpplabel) +
  scale_y_continuous(breaks = gppControl$gppbreaks[2:length(gppControl$gppbreaks)],
                     labels = round(10^(gppControl$gppbreaks[2:length(gppControl$gppbreaks)]))) +
  labs(x = 'Wetland', y = gpplabel)
bargppall

I actually like that that encompasses an area that integrates to total production across the Werai

I can do points as below, but the integration implied above is nice

bargppallpoint <- ggplot(gpp_sf_LMU_wide, 
                         aes(x = fct_reorder(as.factor(wetlandID), logGPP), 
                             y = logGPP, color = logGPP)) +
  geom_linerange(mapping = aes(ymin = lower_logGPP, ymax = upper_logGPP), color = 'grey50') +
  geom_point() + # the geom_bar equivalent when it's not counting cases.
  scale_color_stepsn(colors = gppControl$gpppal,
                     breaks = gppControl$gppbreaks[2:length(gppControl$gppbreaks)],
                     limits = c(min(gppControl$gppbreaks), max(gppControl$gppbreaks)),
                     labels = gppControl$gpplabels,
                     guide = 'legend',
                     name = gpplabel) +
  scale_y_continuous(breaks = gppControl$gppbreaks[2:length(gppControl$gppbreaks)],
                     labels = round(10^(gppControl$gppbreaks[2:length(gppControl$gppbreaks)]))) +
  labs(x = 'Wetland', y = gpplabel)
bargppallpoint

ER

Setup block. Again, this could be sorted with a function, but right now just copy-pasting

erlabel <- 'GPP (kg 02/day)\nat max extent'

er_sf_LMU_wide <- bind_cols(er_sf[,-4], 
                             st_drop_geometry(rename(er_sfPU[,-c(4)], 
                                                     UER = ER, 
                                                     upper_logER = logER)),
                             st_drop_geometry(rename(er_sfPL[,-4], 
                                                     LER = ER,
                                                     lower_logER = logER))) %>%
  mutate(wetlandID = row_number())

Barplot

barerall <- ggplot(er_sf_LMU_wide, 
                    aes(x = fct_reorder(as.factor(wetlandID), logER, .desc = TRUE), y = logER, fill = logER)) +
  geom_col() + # the geom_bar equivalent when it's not counting cases.
  geom_linerange(mapping = aes(ymin = lower_logER, ymax = upper_logER),
                 color = 'grey50', alpha = 0.2) +
  scale_fill_stepsn(colors = erControl$erpal,
                    breaks = erControl$erbreaks[2:length(erControl$erbreaks)],
                    limits = c(min(erControl$erbreaks), max(erControl$erbreaks)),
                    labels = erControl$erlabels,
                    guide = 'legend',
                    name = erlabel) +
  scale_y_continuous(breaks = erControl$erbreaks[2:length(erControl$erbreaks)],
                     labels = round(10^(erControl$erbreaks[2:length(erControl$erbreaks)]))) +
  labs(x = 'Wetland', y = erlabel)
barerall

Both together, with GPP up and ER down

Setup

# Can I make one with them both there going opposite direction?
both_sf_LMU_wide <- bind_cols(mutate(gpp_sf_LMU_wide), 
                              mutate(st_drop_geometry(er_sf_LMU_wide[,-c(8)])))

# Need new labels and breaks. Could cobble, but getting confusing and misaligned
# bothbreaks_log <- c(-rev(erbreaks_log[2:length(erbreaks_log)]), gppbreaks_log)
 
# # and those breaks might not quite yield 10, so maximise the palette differences
# erpal_log <- sequential_hcl(length(erbreaks_log)-1, palette = 'Purples', rev = TRUE)
# I think here I want continuous colors. but maybe still a broken up legend?

# have to do the negatives before delogging
# bothlabels_log <- c(-10^rev(erbreaks_log), 10^gppbreaks_log)
# 
# bothlabels_log <- format(bothlabels_log, big.mark=",", 
                       # scientific=FALSE, trim = TRUE, digits = 0)

# This is really getting complex. Why don't I just use 0,1,10,100,1000,10000?
# They've both been shifted by 1 to log, so
# This is just used to get the labels, the actual log-scale is plotted and so the zeros are done correctly
bothbreaks_log <- c(-10^(4:0), 10^(1:4))
bothbreaks_log[5] <- 0 # Because both were shifted so 1 = 0
bothlabels_log <- as.character(bothbreaks_log)


# erstart <- erlabels_log[1:(length(erlabels_log)-1)]
# erstart[1] <- "0" # instead of 1
# bothlabels_log <- paste0(erstart, ' to ', erlabels_log[2:length(erlabels_log)])
# erlabels_log

# AAAA try to get some lables for the x. I JUST WANT IT TO HAVE THE NUMBER BUT IT WON"T DO IT
reordx <- fct_reorder(as.factor(both_sf_LMU_wide$wetlandID), 
                      both_sf_LMU_wide$logGPP, .desc = TRUE)
# I'm confused. that didn't work. this is anoyuting

Try a plot

barboth <- ggplot(both_sf_LMU_wide) +
  geom_col(mapping = aes(x = fct_reorder(as.factor(wetlandID), 
                                         logER, .desc = TRUE), 
                         y = -logER, fill = -logER)) + # the geom_bar equivalent when it's not counting cases.
  geom_linerange(mapping = aes(x = fct_reorder(as.factor(wetlandID), 
                                               logER, .desc = TRUE),
                               ymin = -lower_logER, ymax = -upper_logER),
                 color = 'grey50', alpha = 0.2) +
  # scale_fill_stepsn(colors = erpal_log,
  #                   breaks = erbreaks_log[2:length(erbreaks_log)],
  #                   limits = c(min(erbreaks_log), max(erbreaks_log)),
  #                   labels = erlabels_log,
  #                   guide = 'legend',
  #                   name = erlabel) +
 
  # scale_fill_stepsn(colors = c(rev(erpal_log), gpppal_log),
  #            breaks = c(-rev(erbreaks_log[2:length(erbreaks_log)]), 0,
  #                       gppbreaks_log[2:length(gppbreaks_log)]),
  #            limits = c(min(-erbreaks_log), max(gppbreaks_log)),
  #            # labels = c(erlabels_log, gpplabels_log),
  #            guide = 'legend',
  #            name = 'kg O2\nat max extent') +
  geom_col(mapping = aes(x = fct_reorder(as.factor(wetlandID), 
                                         logGPP, .desc = TRUE), 
                         y = logGPP, fill = logGPP)) +
  geom_linerange(mapping = aes(x = fct_reorder(as.factor(wetlandID), 
                                               logGPP, .desc = TRUE),
                               ymin = lower_logGPP, ymax = upper_logGPP),
                 color = 'grey50', alpha = 0.2) +
  geom_line(mapping = aes(x = fct_reorder(as.factor(wetlandID), 
                                           logGPP, .desc = TRUE), 
                           y = logGPP-logER, group = NA)) +
  
  # This palette is very slightly different because it's not binned and GPP above uses Emerald instead of green, but it's sure close and way easier
  scale_fill_continuous_diverging(palette = "Purple-Green",
                                  limits = c(-4, 4), 
                                  breaks = -4:4,
                                  labels = bothlabels_log) +
  scale_y_continuous(limits = c(-4, 4),
                     breaks = -4:4,
                     labels = bothlabels_log) +
  # scale_y_continuous(breaks = c(-erbreaks_log[2:length(erbreaks_log)], 
  #                               gppbreaks_log[2:length(gppbreaks_log)]),
  #                    labels = c(-round(10^(erbreaks_log[2:length(erbreaks_log)])), 
  #                               round(10^(gppbreaks_log[2:length(gppbreaks_log)])))) +
  labs(x = 'Wetland (ordered by GPP)', y = 'kg O2', fill = 'kg O2') +
  # argh doesn't work because has been reordered
  # scale_x_discrete(breaks = as.character(c(1, 250, 500, 750, 1000))) +
  theme(legend.position = c(0.75, 0.54)) +
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        legend.position = c(0.75, 0.55))
barboth

The inability to label x in a reasonable way is infuriating. Can I do it differently? Try making a labelled dataset that’s arranged how I was doing fct_reorder.

both_sf_LMU_wide2 <- both_sf_LMU_wide %>% 
  arrange(desc(logGPP)) %>% 
  mutate(GPPrank = row_number())

Make the plot. Interesting that the notebook plotter does a worse job than the plots panel for a script. The funny gaps aren’t really there- to see, run barboth in the console.

barboth <- ggplot(both_sf_LMU_wide2) +
  geom_col(mapping = aes(x = GPPrank, 
                         y = -logER, fill = -logER)) + # the geom_bar equivalent when it's not counting cases.
  geom_linerange(mapping = aes(x = GPPrank,
                               ymin = -lower_logER, ymax = -upper_logER),
                 color = 'grey50', alpha = 0.2) +

geom_col(mapping = aes(x = GPPrank, 
                       y = logGPP, fill = logGPP)) +
  geom_linerange(mapping = aes(x = GPPrank,
                               ymin = lower_logGPP, ymax = upper_logGPP),
                 color = 'grey50', alpha = 0.2) +
  geom_line(mapping = aes(x = GPPrank, 
                          y = logGPP-logER, group = NA)) +
  
  # This palette is very slightly different because it's not binned and GPP above uses Emerald instead of green, but it's sure close and way easier
  scale_fill_continuous_diverging(palette = "Purple-Green",
                                  limits = c(-4, 4), 
                                  breaks = -4:4,
                                  labels = bothlabels_log) +
  scale_y_continuous(limits = c(-4, 4),
                     breaks = -4:4,
                     labels = bothlabels_log) +
  labs(x = 'Wetland (ordered by GPP)', y = 'kg O2', fill = 'kg O2') +
  # scale_x_discrete(breaks = c(1, 250, 500, 750, 1000)) +
  theme_bw(base_size = 8) + theme(legend.position = c(0.75, 0.53),
                                  panel.grid.major = element_blank(),
                                  panel.grid.minor = element_blank())
barboth

Save that as a figure

pdf(file.path(scriptOut, 'Werai_mirrorgram.pdf'), 
    onefile = FALSE, height = 8/2.54, width = 12/2.54, useDingbats = FALSE)
print(barboth)
dev.off()
null device 
          1 
png(file.path(scriptOut, 'Werai_mirrorgram.png'), 
    height = 8/2.54, width = 12/2.54, units = 'in', res = 300)
print(barboth)
dev.off()
null device 
          1 

What about Darren’s fingerprints? They would maybe take the GPP and ER and make a density with them? OUt of what? The wetlands? Could work. Would need to sort out the weighting by volume. it’s already in there, but that means it will give each location the same weight. I think a better thing for the fingerprints would be to remove it, and then give each wetland its predicted per liter rate and weight by volume IE use the straight predictions and then weight by inundaton Should be straightforward, but likely won’t be

Would be a really cool way to look at scenarios. especially if I can do it for whole catchments

BUT, because GPP and ER are both linear fits of temp, they will exactly predict each other according to a linear relationship, and so there won’t be any 2-d variation and all the points will fall on a line. IE for a given GPP there will only be one ER, and so the fingerprint will be a line. Cool idea, but we’d need more info about their variance relative to each other

Annual reporting

What about making some examples to illustrate annual (or arbitrary time period) reporting?

First, establish the time periods and do the aggregations and other data organisation

interDates <- as.POSIXct(c("2014-06-30", "2015-06-30", "2016-06-30", "2017-06-30", "2018-06-30", "2019-06-30"))

tempannual <- tempaggregate(weraiCropTemp, by_t = as.Date(interDates), FUN = mean, na.rm = TRUE)
inunannual <- tempaggregate(weraiCropInun, by_t = as.Date(interDates), FUN = sum, na.rm = TRUE)
gppannual <- tempaggregate(weraiCropPredGPP[1,,], by_t = as.Date(interDates), FUN = sum, na.rm = TRUE)
erannual <- tempaggregate(weraiCropPredER[1,,], by_t = as.Date(interDates), FUN = sum, na.rm = TRUE)

# Data organisation
# AHHH the flopped dims
if (attributes(st_dimensions(gppannual))$name[1] != 'geometry') {
  gppannual <- aperm(gppannual, c(2,1))
}
if (attributes(st_dimensions(erannual))$name[1] != 'geometry') {
  erannual <- aperm(erannual, c(2,1))
}

# GPP
gppYear_sf <- gppannual %>% 
  st_as_sf() %>% 
  pivot_longer(cols = -geometry, names_to = 'WaterYear', values_to = 'GPP') %>%
  mutate(logGPP =  log10(1+GPP)) %>%
  st_as_sf() # REALLY???
number of items to replace is not a multiple of replacement length
gppYear_sf
Simple feature collection with 5815 features and 3 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: 1086148 ymin: -3924954 xmax: 1148710 ymax: -3893940
Projected CRS: GDA94 / Australian Albers
max(gppYear_sf$logGPP) # is within 
[1] 3.412777
gppControl$gppbreaks
[1] 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0
# ER
erYear_sf <- erannual %>% 
  st_as_sf() %>% 
  pivot_longer(cols = -geometry, names_to = 'WaterYear', values_to = 'ER') %>%
  mutate(logER =  log10(1+ER)) %>%
  st_as_sf() # REALLY???
number of items to replace is not a multiple of replacement length
erYear_sf
Simple feature collection with 5815 features and 3 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: 1086148 ymin: -3924954 xmax: 1148710 ymax: -3893940
Projected CRS: GDA94 / Australian Albers
# the range should still work for tthe colors
max(erYear_sf$logER) # is within 
[1] 3.779558
erControl$erbreaks
[1] 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0

I don’t think I use this, but I did make a full-werai aggregation

# aggregate to werai
# temp area-weighted
areas <- tempannual %>%
  st_geometry() %>%
  st_area() %>%
  as.numeric()
tempW <- catchAgg <- catchAggW(strict = tempannual, strictWeights = areas,
                               FUN = mean, summaryPoly = ramsarW1)
  
inunW <- aggregate(inunannual, 
                      by = ramsarW1, 
                      FUN = sum, na.rm = TRUE)

gppW <- aggregate(gppannual, 
                   by = ramsarW1, 
                   FUN = sum, na.rm = TRUE)
erW <- aggregate(erannual, 
                   by = ramsarW1, 
                   FUN = sum, na.rm = TRUE)

Tmap Plots

Make a facetted tmap

Again, can I do this by modifying what I pass to the plotting functions? Almost certainly

The units are weird, because this is added up across bimonths. It’s neither the total or the average.Not really sure what to call it

Change the tmap_mode() depending on the purpose. DO NOT CHANGE TO VIEW AND TRY TO RUN HERE. IF YOU WANT TO VIEW, CHANGE AND RUN IN CONSOLE. The markdown will run it, but incredibly slowly.

GPP

tmap_mode('plot')
tmap mode set to plotting
gppyrlabel <- 'Total Yearly GPP (kg 02)\nat max bimonth extents'

  gppAnnual_tm <- gppYear_sf %>%
    filter(WaterYear != '2014-06-29') %>% # because a 5-panel is ugly
    tm_shape() +
    tm_fill(col = 'logGPP',
            palette = gppControl$gpppal,
            breaks = gppControl$gppbreaks,
            labels = gppControl$gpplabels,
            title = gppyrlabel) + 
    tm_facets(by = 'WaterYear')
  gppAnnual_tm

ER

eryrlabel <- 'Total Yearly ER (kg 02)\nat max bimonth extents'
  
  erAnnual_tm <- erYear_sf %>%
    filter(WaterYear != '2014-06-29') %>% # because a 5-panel is ugly
    tm_shape() +
    tm_fill(col = 'logER',
            palette = erControl$erpal,
            breaks = erControl$erbreaks,
            labels = erControl$erlabels,
            title = eryrlabel) + 
    tm_facets(by = 'WaterYear')
  erAnnual_tm

GGPLOT

gpp

gppAnnual_gg <- gppYear_sf %>%
    filter(WaterYear != '2014-06-29') %>% # because a 5-panel is ugly
    ggplot() +
    geom_sf(mapping = aes(fill = logGPP), color = NA) +
    coord_sf() +
    # Closest to the tmap
    scale_fill_stepsn(colors = gppControl$gpppal,
                      breaks = gppControl$gppbreaks[2:length(gppControl$gppbreaks)],
                      limits = c(min(gppControl$gppbreaks), max(gppControl$gppbreaks)),
                      labels = gppControl$gpplabels,
                      guide = 'legend',
                      name = gppyrlabel) +
    facet_wrap(vars(WaterYear))
  gppAnnual_gg

ER

erAnnual_gg <- erYear_sf %>%
    filter(WaterYear != '2014-06-29') %>% # because a 5-panel is ugly
    ggplot() +
    geom_sf(mapping = aes(fill = logER), color = NA) +
    coord_sf() +
    # Closest to the tmap
    scale_fill_stepsn(colors = erControl$erpal,
                      breaks = erControl$erbreaks[2:length(erControl$erbreaks)],
                      limits = c(min(erControl$erbreaks), max(erControl$erbreaks)),
                      labels = erControl$erlabels,
                      guide = 'legend',
                      name = eryrlabel) +
    facet_wrap(vars(WaterYear))
  erAnnual_gg

Stack 3 years of gpp and ER together for the document

gppAnnual_gg3 <- gppYear_sf %>%
    filter(WaterYear != '2014-06-29' & WaterYear != '2018-06-29') %>% # because a 5-panel is ugly
    ggplot() +
    geom_sf(mapping = aes(fill = logGPP), color = NA) +
    coord_sf() +
    # Closest to the tmap
    scale_fill_stepsn(colors = gppControl$gpppal,
                      breaks = gppControl$gppbreaks[2:length(gppControl$gppbreaks)],
                      limits = c(min(gppControl$gppbreaks), max(gppControl$gppbreaks)),
                      labels = gppControl$gpplabels,
                      guide = 'legend',
                      name = gppyrlabel) +
    facet_grid(WaterYear~.) + 
    theme(legend.position = 'bottom')
  # gppAnnual_gg3 
  
  erAnnual_gg3 <- erYear_sf %>%
    filter(WaterYear != '2014-06-29' & WaterYear != '2018-06-29') %>% # because a 5-panel is ugly
    ggplot() +
    geom_sf(mapping = aes(fill = logER), color = NA) +
    coord_sf() +
    # Closest to the tmap
    scale_fill_stepsn(colors = erControl$erpal,
                      breaks = erControl$erbreaks[2:length(erControl$erbreaks)],
                      limits = c(min(erControl$erbreaks), max(erControl$erbreaks)),
                      labels = erControl$erlabels,
                      guide = 'legend',
                      name = eryrlabel) +
    facet_grid(WaterYear~.) + 
    theme(legend.position = 'bottom')
  # erAnnual_gg3

  annualGPPER <- ggpubr::ggarrange(gppAnnual_gg3 + 
                                     guides(fill = guide_legend(title.position = 'top')) +
                                     theme_grey(base_size = 8) + 
                                     theme(legend.position = 'bottom', 
                                           legend.background = element_blank(),
                                           legend.key.size = unit(0.3, 'cm')), 
                    erAnnual_gg3 + 
                      guides(fill = guide_legend(title.position = 'top')) +
                      theme_grey(base_size = 8) + 
                      theme(legend.position = 'bottom', 
                            legend.background = element_blank(),
                            legend.key.size = unit(0.3, 'cm')),
                    ncol = 2, nrow = 1)
  annualGPPER

Print out the ggplots for the doc

 # Just print the ggplots
  # Can I print those?
  pdf(file.path(scriptOut, 'Werai_gg_Annual.pdf'), 
      onefile = FALSE, height = 12/2.54, width = 16/2.54, useDingbats = FALSE)
  print(annualGPPER)
  dev.off()
null device 
          1 
  
  png(file.path(scriptOut, 'Werai_gg_Annual.png'), 
      height = 12/2.54, width = 16/2.54, units = 'in', res = 300)
  print(annualGPPER)
  dev.off()
null device 
          1 
  
LS0tDQp0aXRsZTogIkxvY2FsIE1ldGFib2xpc20gUGxvdHMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQoNCmBgYHtyIHNldHVwfQ0KIyBzZXQgdGhlIHJvb3QgdG8gdGhlIHByb2plY3QgZGlyZWN0b3J5LCBub3QgdGhlIHJtZCBkaXJlY3RvcnkNCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gcnByb2pyb290OjpmaW5kX3JzdHVkaW9fcm9vdF9maWxlKCkpDQojIGRlZmF1bHQgdG8gbm90IHByaW50aW5nIHRoZSBjb2RlDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IEZBTFNFKQ0KDQojIGxpYnJhcmllcw0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShzdGFycykNCmxpYnJhcnkodG1hcCkNCmxpYnJhcnkodHJhbnNmb3JtcikgIyBub3QgbmVlZGVkIGZvciB0aGlzIGRvYyBhdCBwcmVzZW50Pw0KbGlicmFyeShnZ2FuaW1hdGUpICMgbm90IG5lZWRlZCBoZXJlIGF0IHByZXNlbnQNCmxpYnJhcnkodmlyaWRpcykNCmxpYnJhcnkoY29sb3JzcGFjZSkNCg0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0V9DQojIHNvdXJjZSBpbiB0aGUgZGF0YSBtYW5hZ2VtZW50DQoNCiMgS2luZCBvZiBoYWNreSBjaGVjayB0byBvbmx5IHJ1biBpZiBuZWVkZWQNCmlmICghKCd3ZXJhaUNyb3BJbnVuJyAlaW4lIGxzKCkpKSB7DQogICAgc291cmNlKGZpbGUucGF0aChoZXJlOjpoZXJlKCksICdTY3JpcHRzJywgJ1NjZW5hcmlvcycsICdwbG90dGluZycsICdtZXRhYm9saXNtTG9jYWxBbmRTdGF0aWMuUicpKQ0KfQ0KDQpgYGANCg0KUHVycG9zZSBoZXJlIGlzIHByaW1hcmlseSB0byBnbyB0aHJvdWdoIHNvbWUgcGxvdHMsIGxvb2sgYXQgdGhlbSwgYW5kIG1ha2Ugc3RhdGljIHZlcnNpb25zIGZvciB0aGUgcGFwZXIuDQpUaGlzIGlzIG1vc3RseSBhIGNvcHktcGFzdGUgYW5kIHRyYW5zbGF0ZSBvdmVyIGZyb20gbWV0YWJvbGlzbUxvY2FsQW5kU3RhdGljLiBJJ3ZlIGRvbmUgdGhpcyB0byBzcGxpdCBvdXQgdGhlIGRhdGEgcmVhZC1pbiwgZnVuY3Rpb24gZGVmaW5pdGlvbnMsIGFuZCBwbG90dGluZyAoYW5kIHBhcnRpY3VsYXJseSB0aGUgcGxvdCB0cnlpbmctb3V0KS4gSSd2ZSBiZWVuIHVzaW5nIHRoZXNlIG5vdGVib29rcyBmb3IgcGxvdCB0ZXN0aW5nLCBiZWNhdXNlIGl0J3MgcmVhbGx5IG5pY2UgdG8gYmUgYWJsZSB0byBzY3JvbGwgYXJvdW5kIGFuZCBzZWUgd2hhdCB3ZSd2ZSBkb25lLCByYXRoZXIgdGhhbiBoYXZlIHRvIGtlZXAgcmUtbWFraW5nIHBsb3Qgb2JqZWN0cyB3aGVuIHdlIGZvcmdldCB3aGF0IHRoZXkgbG9vayBsaWtlLg0KDQoqVGhlIGNhdGNoIHdpdGggdGhpcyBhcHByb2FjaCBpcyB0aGUgcXVpY2sgYW5kIGRpcnR5IHByb3RvdHlwaW5nIGlzbid0IHF1aXRlIHNvIHF1aWNrLCBhbmQgdGhpcyBkb2N1bWVudCBlbmRzIHVwIHRha2luZyBGT1JFVkVSIHRvIGxvYWQuKiBNaWdodCBzd2l0Y2ggYmFjaz8gQnV0IGl0IHN1cmUgaXMgbmljZSB0byBhY3R1YWxseSBzZWUgd2hhdCBlYWNoIGZpZ3VyZSBJIG1hZGUgbG9va3MgbGlrZSwgZXNwZWNpYWxseSB3aGVuIHdlJ3JlIGxpa2VseSB0byBkcm9wIHRoaXMgZm9yIGV4dGVuZGVkIHBlcmlvZHMuDQoNClNldCB0aGUgZGF0ZSBmb3IgZXhhbXBsZXMNCmBgYHtyfQ0KYXZhaWxEYXlzIDwtIHN0X2dldF9kaW1lbnNpb25fdmFsdWVzKHdlcmFpQ3JvcFRlbXAsIHdoaWNoID0gJ3RpbWUnKQ0KZGF0ZXdhbnRlZCA8LSBhcy5jaGFyYWN0ZXIoYXZhaWxEYXlzWzE3XSkNCmBgYA0KDQoNCiMjIFN0YXRpYyB2ZXJzaW9ucyB3aXRoIGRyaXZlcnMgYW5kIG91dGNvbWVzDQpNYWtlIGEgc3RhdGljIHZlcnNpb24gdXNpbmcgdGhlIHRtYXAgZnVuY3Rpb25zDQpgYGB7cn0NCnN1cHByZXNzTWVzc2FnZXModG1hcF9tb2RlKCdwbG90JykpDQoNCmFsbGZ1bih0ZW1wT2JqID0gd2VyYWlDcm9wVGVtcCwgdGVtcEF0dCA9IDEsDQogICAgICAgICAgICAgICAgICAgaW51bk9iaiA9IHdlcmFpQ3JvcEludW4sIGludW5BdHQgPSAxLA0KICAgICAgICAgICAgICAgICAgIGdwcE9iaiA9IHdlcmFpQ3JvcFByZWRHUFAsIGdwcEF0dCA9IDEsDQogICAgICAgICAgICAgICAgICAgZXJPYmogPSB3ZXJhaUNyb3BQcmVkRVIsIGVyQXR0ID0gMSwNCiAgICAgICAgICAgICAgICAgICBkYXRld2FudGVkKQ0KDQpgYGANCkNsZWFuIHRoYXQgdXAganVzdCBhIGJpdCwgd2l0aCBhIGdyZXkgYmFja2dyb3VuZC4gcHJvYmFibHkgcHVsbCB0aGUgdGl0bGUgb2ZmIGJ1dCBpdCdzIG5pY2UgdG8gaGF2ZSBhcyByZWZlcmVuY2Ugd2hpbGUgaSdtIG1ha2luZyB0aGVzZQ0KDQpgYGB7cn0NCnRtX2dyaWRfc3RhdGljIDwtIHRtYXBfYXJyYW5nZSh0ZW1wZnVuKHdlcmFpQ3JvcFRlbXAsIDEsIGRhdGV3YW50ZWQpICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRtX2xheW91dChiZy5jb2xvciA9ICJncmV5ODUiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnVuZnVuKHdlcmFpQ3JvcEludW4sIDEsIGRhdGV3YW50ZWQsIHRpdGxlZCA9IEZBTFNFKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0bV9sYXlvdXQoYmcuY29sb3IgPSAiZ3JleTg1IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3BwZnVuKHdlcmFpQ3JvcFByZWRHUFAsIDEsIGRhdGV3YW50ZWQsIHRpdGxlZCA9IEZBTFNFKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0bV9sYXlvdXQoYmcuY29sb3IgPSAiZ3JleTg1IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXJmdW4od2VyYWlDcm9wUHJlZEVSLCAxLCBkYXRld2FudGVkLCB0aXRsZWQgPSBGQUxTRSkgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG1fbGF5b3V0KGJnLmNvbG9yID0gImdyZXk4NSIpKQ0KdG1fZ3JpZF9zdGF0aWMNCmBgYA0KDQpJIHRoaW5rIGZvciBjb21wbGV0ZW5lc3MgbGV0J3MgbWFrZSBhIGdncGxvdCB2ZXJzaW9uIHRvbyBhbmQgc2VlIHdoaWNoIGlzIGJldHRlci4NClRha2VzIG1vcmUgd29yaywgYnV0IGhhdmUgbW9yZSBjb250cm9sIChwcm9iYWJseSBqdXN0IGJlY2F1c2UgSSBzcGVhayBnZ3Bsb3QpDQoNCmBgYHtyfQ0KZ2dfZ3JpZF9zdGF0aWMgPC0gZ2dwdWJyOjpnZ2FycmFuZ2UoDQogIHRlbXBmdW4od2VyYWlDcm9wVGVtcCwgMSwgZGF0ZXdhbnRlZCwgcGxvdFBrZyA9ICdnZ3Bsb3QnKSArIA0KICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlLnBvc2l0aW9uID0gJ3RvcCcpKSArDQogICAgdGhlbWVfZ3JleShiYXNlX3NpemUgPSA4KSArIA0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nLCANCiAgICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuMywgJ2NtJykpLA0KICBpbnVuZnVuKHdlcmFpQ3JvcEludW4sIDEsIGRhdGV3YW50ZWQsIHRpdGxlZCA9IEZBTFNFLCBwbG90UGtnID0gJ2dncGxvdCcpICsgDQogICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQodGl0bGUucG9zaXRpb24gPSAndG9wJykpICsNCiAgICB0aGVtZV9ncmV5KGJhc2Vfc2l6ZSA9IDgpICsgDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ2JvdHRvbScsIA0KICAgICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC4zLCAnY20nKSksDQogIGdwcGZ1bih3ZXJhaUNyb3BQcmVkR1BQLCAxLCBkYXRld2FudGVkLCB0aXRsZWQgPSBGQUxTRSwgcGxvdFBrZyA9ICdnZ3Bsb3QnKSArIA0KICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlLnBvc2l0aW9uID0gJ3RvcCcpKSArDQogICAgdGhlbWVfZ3JleShiYXNlX3NpemUgPSA4KSArIA0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nLCANCiAgICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuMywgJ2NtJykpLA0KICBlcmZ1bih3ZXJhaUNyb3BQcmVkRVIsIDEsIGRhdGV3YW50ZWQsIHRpdGxlZCA9IEZBTFNFLCBwbG90UGtnID0gJ2dncGxvdCcpICsgDQogICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQodGl0bGUucG9zaXRpb24gPSAndG9wJykpICsNCiAgICB0aGVtZV9ncmV5KGJhc2Vfc2l6ZSA9IDgpICsgDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ2JvdHRvbScsIA0KICAgICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC4zLCAnY20nKSksDQogIG5jb2wgPSAyLCBucm93ID0gMikNCnByaW50KGdnX2dyaWRfc3RhdGljKQ0KDQpgYGANCg0KQ29kZSBibG9jayB0byBwcmludCBhbmQgbWFrZSB0aG9zZSBpbnRvIGZpZ3MNCmBgYHtyfQ0KcGRmKGZpbGUucGF0aChzY3JpcHRPdXQsICdXZXJhaV90bS5wZGYnKSwgDQogICAgb25lZmlsZSA9IEZBTFNFLCBoZWlnaHQgPSAxMi8yLjU0LCB3aWR0aCA9IDE2LzIuNTQsIHVzZURpbmdiYXRzID0gRkFMU0UpDQpwcmludCh0bV9ncmlkX3N0YXRpYykNCmRldi5vZmYoKQ0KDQpwbmcoZmlsZS5wYXRoKHNjcmlwdE91dCwgJ1dlcmFpX3RtLnBuZycpLCANCiAgICBoZWlnaHQgPSAxMi8yLjU0LCB3aWR0aCA9IDE2LzIuNTQsIHVuaXRzID0gJ2luJywgcmVzID0gMzAwKQ0KcHJpbnQodG1fZ3JpZF9zdGF0aWMpDQpkZXYub2ZmKCkNCg0KIyBDYW4gSSBwcmludCB0aG9zZT8NCnBkZihmaWxlLnBhdGgoc2NyaXB0T3V0LCAnV2VyYWlfZ2cucGRmJyksIA0KICAgIG9uZWZpbGUgPSBGQUxTRSwgaGVpZ2h0ID0gMTIvMi41NCwgd2lkdGggPSAxNi8yLjU0LCB1c2VEaW5nYmF0cyA9IEZBTFNFKQ0KcHJpbnQoZ2dfZ3JpZF9zdGF0aWMpDQpkZXYub2ZmKCkNCg0KcG5nKGZpbGUucGF0aChzY3JpcHRPdXQsICdXZXJhaV9nZy5wbmcnKSwgDQogICAgaGVpZ2h0ID0gMTIvMi41NCwgd2lkdGggPSAxNi8yLjU0LCB1bml0cyA9ICdpbicsIHJlcyA9IDMwMCkNCnByaW50KGdnX2dyaWRfc3RhdGljKQ0KZGV2Lm9mZigpDQpgYGANCg0KIyMgVW5jZXJ0YWludHkNCg0KSSB0aGluayBub3cgdGhhdCBJIGhhdmUgdGhlIGZ1bmN0aW9ucywgdGhlcmUncyBhIHNsaWNrZXIgd2F5IHRvIGRvIHRoaXMsIGJ1dCBJIGRvbid0IHdhbnQgdG8gcmVpbnZlbnQgdGhlIHdoZWVsIGF0IHRoaXMgcG9pbnQuIE1pZ2h0IHdhbnQgdG8gdHVybiB0aGlzIGludG8gYSBmdW5jdGlvbj8NCg0KRVINCmBgYHtyfQ0KIyBFUg0KZXJfc2YgPC0gd2VyYWlDcm9wUHJlZEVSWzEsLF0gJT4lIA0KICBzdF9hc19zZigpICU+JSANCiAgc2VsZWN0KGFsbF9vZihkYXRld2FudGVkKSkgJT4lDQogIHJlbmFtZShFUiA9IDEpICU+JQ0KICBtdXRhdGUobG9nRVIgPSBsb2cxMCgxK0VSKSwgdHlwZSA9ICdlc3RpbWF0ZScpDQplcl9zZlBVIDwtIHdlcmFpQ3JvcFByZWRFUlsyLCxdICU+JSANCiAgc3RfYXNfc2YoKSAlPiUgDQogIHNlbGVjdChhbGxfb2YoZGF0ZXdhbnRlZCkpICU+JQ0KICByZW5hbWUoRVIgPSAxKSAlPiUNCiAgbXV0YXRlKGxvZ0VSID0gbG9nMTAoMStFUiksIHR5cGUgPSAndXBwZXInKQ0KZXJfc2ZQTCA8LSB3ZXJhaUNyb3BQcmVkRVJbMywsXSAlPiUgDQogIHN0X2FzX3NmKCkgJT4lIA0KICBzZWxlY3QoYWxsX29mKGRhdGV3YW50ZWQpKSAlPiUNCiAgcmVuYW1lKEVSID0gMSkgJT4lDQogIG11dGF0ZShsb2dFUiA9IGxvZzEwKDErRVIpLCB0eXBlID0gJ2xvd2VyJykNCg0KZXJfc2ZfTE1VIDwtIGJpbmRfcm93cyhlcl9zZiwgZXJfc2ZQVSwgZXJfc2ZQTCkgJT4lDQogIG11dGF0ZShuYW1lZFBJID0gaWZlbHNlKHR5cGUgPT0gJ2xvd2VyJywgIkxvd2VyIDk1JSBQSSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZSh0eXBlID09ICdlc3RpbWF0ZScsICJFc3RpbWF0ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UodHlwZSA9PSAndXBwZXInLCAiVXBwZXIgOTUlIFBJIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDUkVXVVAnKSkpKQ0KDQplcmxhYmVsIDwtICdFUiAoa2cgMDIvZGF5KVxuYXQgbWF4IGV4dGVudCcNCg0KIyBDYW4gZ2V0IGF3YXkgd2l0aCB0aGlzIGJlY2F1c2UgcHJldHR5IHJhbmdlcyB3b3JrIG91dCBPSw0KZXJDb250cm9sIDwtIGVyc2V0dXAoZGF0YSA9IHdlcmFpQ3JvcFByZWRFUiwgYXR0bnVtID0gMSkNCg0KICBlcl9nZ191bmNlcnRhaW4gPC0gZ2dwbG90KCkgKw0KICAgIGdlb21fc2YoZGF0YSA9IGVyX3NmX0xNVSwgbWFwcGluZyA9IGFlcyhmaWxsID0gbG9nRVIpLCBjb2xvciA9IE5BKSArDQogICAgIyBnZW9tX3NmX2xhYmVsKGRhdGEgPSBsdGltTm9Ob3J0aCwgbWFwcGluZyA9IGFlcyhsYWJlbCA9IFZhbGxleU5hbWUpKSArDQogICAgY29vcmRfc2YoKSArDQogICAgIyBDbG9zZXN0IHRvIHRoZSB0bWFwDQogICAgc2NhbGVfZmlsbF9zdGVwc24oY29sb3JzID0gZXJDb250cm9sJGVycGFsLA0KICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGVyQ29udHJvbCRlcmJyZWFrc1syOmxlbmd0aChlckNvbnRyb2wkZXJicmVha3MpXSwNCiAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKG1pbihlckNvbnRyb2wkZXJicmVha3MpLCBtYXgoZXJDb250cm9sJGVyYnJlYWtzKSksDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gZXJDb250cm9sJGVybGFiZWxzLA0KICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gJ2xlZ2VuZCcsDQogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9IGVybGFiZWwpICsNCiAgICBmYWNldF9ncmlkKGZjdF9yZW9yZGVyKG5hbWVkUEksIC54ID0gbG9nRVIsIC5mdW4gPSBtYXgpfi4pICMgbG93ZXIsIGVzdGltYXQgYW5kIHVwcGVyIHNob3VsZCBhbHdheXMgZmFsbCBpbiB0aGF0IG9yZGVyIGZvciB0aGVpciBtYXhlcw0KDQplcl9nZ191bmNlcnRhaW4NCg0KYGBgDQoNCkdQUA0KYGBge3J9DQojIEdQUA0KIyBJJ20gc3VyZSB0aGdwcGUncyBhIHNsaWNrIHdheSB0byBzZWxlY3QgYWxsIHRocmVlIGF0dHJpYnV0ZXMgYW5kIGRvIHRoaXMgaW4gb25lDQojIGdvLCBidXQgSSBqdXN0IG5lZWQgdG8gZ2V0IHRoaXMgZG9uZQ0KZ3BwX3NmIDwtIHdlcmFpQ3JvcFByZWRHUFBbMSwsXSAlPiUgDQogIHN0X2FzX3NmKCkgJT4lIA0KICBzZWxlY3QoYWxsX29mKGRhdGV3YW50ZWQpKSAlPiUNCiAgcmVuYW1lKEdQUCA9IDEpICU+JQ0KICBtdXRhdGUobG9nR1BQID0gbG9nMTAoMStHUFApLCB0eXBlID0gJ2VzdGltYXRlJykNCmdwcF9zZlBVIDwtIHdlcmFpQ3JvcFByZWRHUFBbMiwsXSAlPiUgDQogIHN0X2FzX3NmKCkgJT4lIA0KICBzZWxlY3QoYWxsX29mKGRhdGV3YW50ZWQpKSAlPiUNCiAgcmVuYW1lKEdQUCA9IDEpICU+JQ0KICBtdXRhdGUobG9nR1BQID0gbG9nMTAoMStHUFApLCB0eXBlID0gJ3VwcGVyJykNCmdwcF9zZlBMIDwtIHdlcmFpQ3JvcFByZWRHUFBbMywsXSAlPiUgDQogIHN0X2FzX3NmKCkgJT4lIA0KICBzZWxlY3QoYWxsX29mKGRhdGV3YW50ZWQpKSAlPiUNCiAgcmVuYW1lKEdQUCA9IDEpICU+JQ0KICBtdXRhdGUobG9nR1BQID0gbG9nMTAoMStHUFApLCB0eXBlID0gJ2xvd2VyJykNCg0KZ3BwX3NmX0xNVSA8LSBiaW5kX3Jvd3MoZ3BwX3NmLCBncHBfc2ZQVSwgZ3BwX3NmUEwpICU+JQ0KICBtdXRhdGUobmFtZWRQSSA9IGlmZWxzZSh0eXBlID09ICdsb3dlcicsICJMb3dlciA5NSUgUEkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UodHlwZSA9PSAnZXN0aW1hdGUnLCAiRXN0aW1hdGUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHR5cGUgPT0gJ3VwcGVyJywgIlVwcGVyIDk1JSBQSSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQ1JFV1VQJykpKSkNCg0KZ3BwbGFiZWwgPC0gJ0dQUCAoa2cgMDIvZGF5KVxuYXQgbWF4IGV4dGVudCcNCg0KZ3BwQ29udHJvbCA8LSBncHBzZXR1cCh3ZXJhaUNyb3BQcmVkR1BQLCBhdHRudW0gID0gMSkNCg0KZ3BwX2dnX3VuY2VydGFpbiA8LSBnZ3Bsb3QoKSArDQogIGdlb21fc2YoZGF0YSA9IGdwcF9zZl9MTVUsIG1hcHBpbmcgPSBhZXMoZmlsbCA9IGxvZ0dQUCksIGNvbG9yID0gTkEpICsNCiAgIyBnZW9tX3NmX2xhYmVsKGRhdGEgPSBsdGltTm9Ob3J0aCwgbWFwcGluZyA9IGFlcyhsYWJlbCA9IFZhbGxleU5hbWUpKSArDQogIGNvb3JkX3NmKCkgKw0KICAjIENsb3Nlc3QgdG8gdGhlIHRtYXANCiAgc2NhbGVfZmlsbF9zdGVwc24oY29sb3JzID0gZ3BwQ29udHJvbCRncHBwYWwsDQogICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGdwcENvbnRyb2wkZ3BwYnJlYWtzWzI6bGVuZ3RoKGdwcENvbnRyb2wkZ3BwYnJlYWtzKV0sDQogICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMobWluKGdwcENvbnRyb2wkZ3BwYnJlYWtzKSwgbWF4KGdwcENvbnRyb2wkZ3BwYnJlYWtzKSksDQogICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGdwcENvbnRyb2wkZ3BwbGFiZWxzLA0KICAgICAgICAgICAgICAgICAgICBndWlkZSA9ICdsZWdlbmQnLA0KICAgICAgICAgICAgICAgICAgICBuYW1lID0gZ3BwbGFiZWwpICsNCiAgZmFjZXRfZ3JpZChmY3RfcmVvcmRlcihuYW1lZFBJLCAueCA9IGxvZ0dQUCwgLmZ1biA9IG1heCl+LikgIyBsb3dlciwgZXN0aW1hdCBhbmQgdXBwZ3BwIHNob3VsZCBhbHdheXMgZmFsbCBpbiB0aGF0IG9yZGdwcCBmb3IgdGhlaXIgbWF4ZXMNCg0KZ3BwX2dnX3VuY2VydGFpbg0KYGBgDQoNCkNvbWJpbmUgdGhlIEVSIGFuZCBHUFAgYW5kIHNhdmUgYXMgZmlndXJlcw0KYGBge3J9DQpib3RoX3VuY2VydGFpbiA8LSBnZ3B1YnI6OmdnYXJyYW5nZShncHBfZ2dfdW5jZXJ0YWluICsgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlLnBvc2l0aW9uID0gJ3RvcCcpKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoZW1lX2dyZXkoYmFzZV9zaXplID0gOCkgKyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ2JvdHRvbScsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjMsICdjbScpKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlcl9nZ191bmNlcnRhaW4gKyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQodGl0bGUucG9zaXRpb24gPSAndG9wJykpICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlbWVfZ3JleShiYXNlX3NpemUgPSA4KSArIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuMywgJ2NtJykpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDIsIG5yb3cgPSAxKQ0KYm90aF91bmNlcnRhaW4NCg0KIyBKdXN0IHByaW50IHRoZSBnZ3Bsb3RzDQojIENhbiBJIHByaW50IHRob3NlPw0KcGRmKGZpbGUucGF0aChzY3JpcHRPdXQsICdXZXJhaV91bmNlcnRhaW50eS5wZGYnKSwgDQogICAgb25lZmlsZSA9IEZBTFNFLCBoZWlnaHQgPSAxMi8yLjU0LCB3aWR0aCA9IDE2LzIuNTQsIHVzZURpbmdiYXRzID0gRkFMU0UpDQpwcmludChib3RoX3VuY2VydGFpbikNCmRldi5vZmYoKQ0KDQpwbmcoZmlsZS5wYXRoKHNjcmlwdE91dCwgJ1dlcmFpX3VuY2VydGFpbnR5LnBuZycpLCANCiAgICBoZWlnaHQgPSAxMi8yLjU0LCB3aWR0aCA9IDE2LzIuNTQsIHVuaXRzID0gJ2luJywgcmVzID0gMzAwKQ0KcHJpbnQoYm90aF91bmNlcnRhaW4pDQpkZXYub2ZmKCkNCmBgYA0KDQojIyBCYXIgY2hhcnRzDQptYWtlIGEgd2lkZSB2ZXJzaW9uIG9mIHRoZSBkYXRhIHNvIEkgY2FuIGhhdmUgbWlucyBhbmQgbWF4ZXMNCk9SLCBzaG91bGQgSSBkbyBhbGwgb2YgdGhlbSBhbmQgZmN0X29yZGVyIHRoZW0/DQoNCkpvaW5pbmcgc2VlbXMgc2FmZSwgYnV0IGdlb2dyYXBoaWMgam9pbnMgYXJlIGEgbWVzcy4gSXQgZG9lc24ndCB3b3JrIGF0IGFsbC4NCmJpbmRpbmcgY29scyBpcyBhY3R1YWxseSBzYWZlciBhbmQgd29ya3MNCiANCiMjIyBHUFANCg0KYGBge3J9DQpncHBfc2ZfTE1VX3dpZGUgPC0gYmluZF9jb2xzKGdwcF9zZlssLTRdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0X2Ryb3BfZ2VvbWV0cnkocmVuYW1lKGdwcF9zZlBVWywtYyg0KV0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFVHUFAgPSBHUFAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVwcGVyX2xvZ0dQUCA9IGxvZ0dQUCkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RfZHJvcF9nZW9tZXRyeShyZW5hbWUoZ3BwX3NmUExbLC00XSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTEdQUCA9IEdQUCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb3dlcl9sb2dHUFAgPSBsb2dHUFApKSkgJT4lDQogIG11dGF0ZSh3ZXRsYW5kSUQgPSByb3dfbnVtYmVyKCkpDQpgYGANCg0KTWF5YmUgZm9yIGEgc3Vic2V0IG9mIHdldGxhbmRzPw0KDQpgYGB7cn0NCiMgR3JhYiBzb21lIHNldCBvZiB3ZXRsYW5kcw0Kd2hpY2goZ3BwX3NmJGdlb21ldHJ5ID09IGdwcF9zZl9MTVUkZ2VvbWV0cnlbM10pDQpleGFtcGxlV2V0cyA8LSBjKDE6MTAwKQ0KYGBgDQoNCmBgYHtyfQ0KYmFyZ3BwIDwtIGdncGxvdChncHBfc2ZfTE1VX3dpZGVbZXhhbXBsZVdldHMsIF0sIA0KICAgICAgICAgICAgICAgICBhZXMoeCA9IHdldGxhbmRJRCwgeSA9IGxvZ0dQUCwgZmlsbCA9IGxvZ0dQUCkpICsNCiAgZ2VvbV9jb2woKSArICMgdGhlIGdlb21fYmFyIGVxdWl2YWxlbnQgd2hlbiBpdCdzIG5vdCBjb3VudGluZyBjYXNlcy4NCiAgZ2VvbV9lcnJvcmJhcihtYXBwaW5nID0gYWVzKHltaW4gPSBsb3dlcl9sb2dHUFAsIHltYXggPSB1cHBlcl9sb2dHUFApKSArDQogIHNjYWxlX2ZpbGxfc3RlcHNuKGNvbG9ycyA9IGdwcENvbnRyb2wkZ3BwcGFsLA0KICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBncHBDb250cm9sJGdwcGJyZWFrc1syOmxlbmd0aChncHBDb250cm9sJGdwcGJyZWFrcyldLA0KICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKG1pbihncHBDb250cm9sJGdwcGJyZWFrcyksIG1heChncHBDb250cm9sJGdwcGJyZWFrcykpLA0KICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBncHBDb250cm9sJGdwcGxhYmVscywNCiAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSAnbGVnZW5kJywNCiAgICAgICAgICAgICAgICAgICAgbmFtZSA9IGdwcGxhYmVsKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBncHBDb250cm9sJGdwcGJyZWFrc1syOmxlbmd0aChncHBDb250cm9sJGdwcGJyZWFrcyldLA0KICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcm91bmQoMTBeKGdwcENvbnRyb2wkZ3BwYnJlYWtzWzI6bGVuZ3RoKGdwcENvbnRyb2wkZ3BwYnJlYWtzKV0pKSkgKw0KICBsYWJzKHggPSAnV2V0bGFuZCcsIHkgPSBncHBsYWJlbCkNCmJhcmdwcA0KDQpgYGANCg0KIEkgYXNzdW1lIGl0IGlzIGEgbWVzIHRvIGp1c3QgdGhyb3cgdGhlbSBhbGwgb24NCiBhY3R1YWxseSwgd2l0aCBhIGZhaXIgYW1vdW50IG9mIG1vbmtleWluZyB3aXRoIHRoZSBsb29rLCB0aGF0J3MgT0sNCiANCmBgYHtyfQ0KYmFyZ3BwYWxsIDwtIGdncGxvdChncHBfc2ZfTE1VX3dpZGUsIA0KICAgICAgICAgICAgICAgICBhZXMoeCA9IGZjdF9yZW9yZGVyKGFzLmZhY3Rvcih3ZXRsYW5kSUQpLCBsb2dHUFAsIC5kZXNjID0gVFJVRSksIHkgPSBsb2dHUFAsIGZpbGwgPSBsb2dHUFApKSArDQogIGdlb21fY29sKCkgKyAjIHRoZSBnZW9tX2JhciBlcXVpdmFsZW50IHdoZW4gaXQncyBub3QgY291bnRpbmcgY2FzZXMuDQogIGdlb21fbGluZXJhbmdlKG1hcHBpbmcgPSBhZXMoeW1pbiA9IGxvd2VyX2xvZ0dQUCwgeW1heCA9IHVwcGVyX2xvZ0dQUCksDQogICAgICAgICAgICAgICAgIGNvbG9yID0gJ2dyZXk1MCcsIGFscGhhID0gMC4yKSArDQogIHNjYWxlX2ZpbGxfc3RlcHNuKGNvbG9ycyA9IGdwcENvbnRyb2wkZ3BwcGFsLA0KICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBncHBDb250cm9sJGdwcGJyZWFrc1syOmxlbmd0aChncHBDb250cm9sJGdwcGJyZWFrcyldLA0KICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKG1pbihncHBDb250cm9sJGdwcGJyZWFrcyksIG1heChncHBDb250cm9sJGdwcGJyZWFrcykpLA0KICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBncHBDb250cm9sJGdwcGxhYmVscywNCiAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSAnbGVnZW5kJywNCiAgICAgICAgICAgICAgICAgICAgbmFtZSA9IGdwcGxhYmVsKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBncHBDb250cm9sJGdwcGJyZWFrc1syOmxlbmd0aChncHBDb250cm9sJGdwcGJyZWFrcyldLA0KICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcm91bmQoMTBeKGdwcENvbnRyb2wkZ3BwYnJlYWtzWzI6bGVuZ3RoKGdwcENvbnRyb2wkZ3BwYnJlYWtzKV0pKSkgKw0KICBsYWJzKHggPSAnV2V0bGFuZCcsIHkgPSBncHBsYWJlbCkNCmJhcmdwcGFsbA0KYGBgDQogDQpJIGFjdHVhbGx5IGxpa2UgdGhhdCB0aGF0IGVuY29tcGFzc2VzIGFuIGFyZWEgdGhhdCBpbnRlZ3JhdGVzIHRvIHRvdGFsIHByb2R1Y3Rpb24gYWNyb3NzIHRoZSBXZXJhaQ0KDQpJIGNhbiBkbyBwb2ludHMgYXMgYmVsb3csIGJ1dCB0aGUgaW50ZWdyYXRpb24gaW1wbGllZCBhYm92ZSBpcyBuaWNlDQoNCmBgYHtyfQ0KYmFyZ3BwYWxscG9pbnQgPC0gZ2dwbG90KGdwcF9zZl9MTVVfd2lkZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBmY3RfcmVvcmRlcihhcy5mYWN0b3Iod2V0bGFuZElEKSwgbG9nR1BQKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBsb2dHUFAsIGNvbG9yID0gbG9nR1BQKSkgKw0KICBnZW9tX2xpbmVyYW5nZShtYXBwaW5nID0gYWVzKHltaW4gPSBsb3dlcl9sb2dHUFAsIHltYXggPSB1cHBlcl9sb2dHUFApLCBjb2xvciA9ICdncmV5NTAnKSArDQogIGdlb21fcG9pbnQoKSArICMgdGhlIGdlb21fYmFyIGVxdWl2YWxlbnQgd2hlbiBpdCdzIG5vdCBjb3VudGluZyBjYXNlcy4NCiAgc2NhbGVfY29sb3Jfc3RlcHNuKGNvbG9ycyA9IGdwcENvbnRyb2wkZ3BwcGFsLA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gZ3BwQ29udHJvbCRncHBicmVha3NbMjpsZW5ndGgoZ3BwQ29udHJvbCRncHBicmVha3MpXSwNCiAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMobWluKGdwcENvbnRyb2wkZ3BwYnJlYWtzKSwgbWF4KGdwcENvbnRyb2wkZ3BwYnJlYWtzKSksDQogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBncHBDb250cm9sJGdwcGxhYmVscywNCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gJ2xlZ2VuZCcsDQogICAgICAgICAgICAgICAgICAgICBuYW1lID0gZ3BwbGFiZWwpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGdwcENvbnRyb2wkZ3BwYnJlYWtzWzI6bGVuZ3RoKGdwcENvbnRyb2wkZ3BwYnJlYWtzKV0sDQogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSByb3VuZCgxMF4oZ3BwQ29udHJvbCRncHBicmVha3NbMjpsZW5ndGgoZ3BwQ29udHJvbCRncHBicmVha3MpXSkpKSArDQogIGxhYnMoeCA9ICdXZXRsYW5kJywgeSA9IGdwcGxhYmVsKQ0KYmFyZ3BwYWxscG9pbnQNCmBgYA0KDQojIyMgRVINCg0KU2V0dXAgYmxvY2suIEFnYWluLCB0aGlzIGNvdWxkIGJlIHNvcnRlZCB3aXRoIGEgZnVuY3Rpb24sIGJ1dCByaWdodCBub3cganVzdCBjb3B5LXBhc3RpbmcNCg0KYGBge3J9DQplcmxhYmVsIDwtICdHUFAgKGtnIDAyL2RheSlcbmF0IG1heCBleHRlbnQnDQoNCmVyX3NmX0xNVV93aWRlIDwtIGJpbmRfY29scyhlcl9zZlssLTRdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RfZHJvcF9nZW9tZXRyeShyZW5hbWUoZXJfc2ZQVVssLWMoNCldLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVUVSID0gRVIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cHBlcl9sb2dFUiA9IGxvZ0VSKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0X2Ryb3BfZ2VvbWV0cnkocmVuYW1lKGVyX3NmUExbLC00XSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExFUiA9IEVSLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb3dlcl9sb2dFUiA9IGxvZ0VSKSkpICU+JQ0KICBtdXRhdGUod2V0bGFuZElEID0gcm93X251bWJlcigpKQ0KYGBgDQoNCkJhcnBsb3QNCmBgYHtyfQ0KYmFyZXJhbGwgPC0gZ2dwbG90KGVyX3NmX0xNVV93aWRlLCANCiAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBmY3RfcmVvcmRlcihhcy5mYWN0b3Iod2V0bGFuZElEKSwgbG9nRVIsIC5kZXNjID0gVFJVRSksIHkgPSBsb2dFUiwgZmlsbCA9IGxvZ0VSKSkgKw0KICBnZW9tX2NvbCgpICsgIyB0aGUgZ2VvbV9iYXIgZXF1aXZhbGVudCB3aGVuIGl0J3Mgbm90IGNvdW50aW5nIGNhc2VzLg0KICBnZW9tX2xpbmVyYW5nZShtYXBwaW5nID0gYWVzKHltaW4gPSBsb3dlcl9sb2dFUiwgeW1heCA9IHVwcGVyX2xvZ0VSKSwNCiAgICAgICAgICAgICAgICAgY29sb3IgPSAnZ3JleTUwJywgYWxwaGEgPSAwLjIpICsNCiAgc2NhbGVfZmlsbF9zdGVwc24oY29sb3JzID0gZXJDb250cm9sJGVycGFsLA0KICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBlckNvbnRyb2wkZXJicmVha3NbMjpsZW5ndGgoZXJDb250cm9sJGVyYnJlYWtzKV0sDQogICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMobWluKGVyQ29udHJvbCRlcmJyZWFrcyksIG1heChlckNvbnRyb2wkZXJicmVha3MpKSwNCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gZXJDb250cm9sJGVybGFiZWxzLA0KICAgICAgICAgICAgICAgICAgICBndWlkZSA9ICdsZWdlbmQnLA0KICAgICAgICAgICAgICAgICAgICBuYW1lID0gZXJsYWJlbCkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gZXJDb250cm9sJGVyYnJlYWtzWzI6bGVuZ3RoKGVyQ29udHJvbCRlcmJyZWFrcyldLA0KICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcm91bmQoMTBeKGVyQ29udHJvbCRlcmJyZWFrc1syOmxlbmd0aChlckNvbnRyb2wkZXJicmVha3MpXSkpKSArDQogIGxhYnMoeCA9ICdXZXRsYW5kJywgeSA9IGVybGFiZWwpDQpiYXJlcmFsbA0KYGBgDQoNCiMjIyBCb3RoIHRvZ2V0aGVyLCB3aXRoIEdQUCB1cCBhbmQgRVIgZG93bg0KDQpTZXR1cA0KYGBge3J9DQojIENhbiBJIG1ha2Ugb25lIHdpdGggdGhlbSBib3RoIHRoZXJlIGdvaW5nIG9wcG9zaXRlIGRpcmVjdGlvbj8NCmJvdGhfc2ZfTE1VX3dpZGUgPC0gYmluZF9jb2xzKG11dGF0ZShncHBfc2ZfTE1VX3dpZGUpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShzdF9kcm9wX2dlb21ldHJ5KGVyX3NmX0xNVV93aWRlWywtYyg4KV0pKSkNCg0KIyBOZWVkIG5ldyBsYWJlbHMgYW5kIGJyZWFrcy4gQ291bGQgY29iYmxlLCBidXQgZ2V0dGluZyBjb25mdXNpbmcgYW5kIG1pc2FsaWduZWQNCiMgYm90aGJyZWFrc19sb2cgPC0gYygtcmV2KGVyYnJlYWtzX2xvZ1syOmxlbmd0aChlcmJyZWFrc19sb2cpXSksIGdwcGJyZWFrc19sb2cpDQogDQojICMgYW5kIHRob3NlIGJyZWFrcyBtaWdodCBub3QgcXVpdGUgeWllbGQgMTAsIHNvIG1heGltaXNlIHRoZSBwYWxldHRlIGRpZmZlcmVuY2VzDQojIGVycGFsX2xvZyA8LSBzZXF1ZW50aWFsX2hjbChsZW5ndGgoZXJicmVha3NfbG9nKS0xLCBwYWxldHRlID0gJ1B1cnBsZXMnLCByZXYgPSBUUlVFKQ0KIyBJIHRoaW5rIGhlcmUgSSB3YW50IGNvbnRpbnVvdXMgY29sb3JzLiBidXQgbWF5YmUgc3RpbGwgYSBicm9rZW4gdXAgbGVnZW5kPw0KDQojIGhhdmUgdG8gZG8gdGhlIG5lZ2F0aXZlcyBiZWZvcmUgZGVsb2dnaW5nDQojIGJvdGhsYWJlbHNfbG9nIDwtIGMoLTEwXnJldihlcmJyZWFrc19sb2cpLCAxMF5ncHBicmVha3NfbG9nKQ0KIyANCiMgYm90aGxhYmVsc19sb2cgPC0gZm9ybWF0KGJvdGhsYWJlbHNfbG9nLCBiaWcubWFyaz0iLCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAjIHNjaWVudGlmaWM9RkFMU0UsIHRyaW0gPSBUUlVFLCBkaWdpdHMgPSAwKQ0KDQojIFRoaXMgaXMgcmVhbGx5IGdldHRpbmcgY29tcGxleC4gV2h5IGRvbid0IEkganVzdCB1c2UgMCwxLDEwLDEwMCwxMDAwLDEwMDAwPw0KIyBUaGV5J3ZlIGJvdGggYmVlbiBzaGlmdGVkIGJ5IDEgdG8gbG9nLCBzbw0KIyBUaGlzIGlzIGp1c3QgdXNlZCB0byBnZXQgdGhlIGxhYmVscywgdGhlIGFjdHVhbCBsb2ctc2NhbGUgaXMgcGxvdHRlZCBhbmQgc28gdGhlIHplcm9zIGFyZSBkb25lIGNvcnJlY3RseQ0KYm90aGJyZWFrc19sb2cgPC0gYygtMTBeKDQ6MCksIDEwXigxOjQpKQ0KYm90aGJyZWFrc19sb2dbNV0gPC0gMCAjIEJlY2F1c2UgYm90aCB3ZXJlIHNoaWZ0ZWQgc28gMSA9IDANCmJvdGhsYWJlbHNfbG9nIDwtIGFzLmNoYXJhY3Rlcihib3RoYnJlYWtzX2xvZykNCg0KDQojIGVyc3RhcnQgPC0gZXJsYWJlbHNfbG9nWzE6KGxlbmd0aChlcmxhYmVsc19sb2cpLTEpXQ0KIyBlcnN0YXJ0WzFdIDwtICIwIiAjIGluc3RlYWQgb2YgMQ0KIyBib3RobGFiZWxzX2xvZyA8LSBwYXN0ZTAoZXJzdGFydCwgJyB0byAnLCBlcmxhYmVsc19sb2dbMjpsZW5ndGgoZXJsYWJlbHNfbG9nKV0pDQojIGVybGFiZWxzX2xvZw0KDQojIEFBQUEgdHJ5IHRvIGdldCBzb21lIGxhYmxlcyBmb3IgdGhlIHguIEkgSlVTVCBXQU5UIElUIFRPIEhBVkUgVEhFIE5VTUJFUiBCVVQgSVQgV09OIlQgRE8gSVQNCnJlb3JkeCA8LSBmY3RfcmVvcmRlcihhcy5mYWN0b3IoYm90aF9zZl9MTVVfd2lkZSR3ZXRsYW5kSUQpLCANCiAgICAgICAgICAgICAgICAgICAgICBib3RoX3NmX0xNVV93aWRlJGxvZ0dQUCwgLmRlc2MgPSBUUlVFKQ0KIyBJJ20gY29uZnVzZWQuIHRoYXQgZGlkbid0IHdvcmsuIHRoaXMgaXMgYW5veXV0aW5nDQpgYGANCg0KVHJ5IGEgcGxvdA0KDQpgYGB7cn0NCmJhcmJvdGggPC0gZ2dwbG90KGJvdGhfc2ZfTE1VX3dpZGUpICsNCiAgZ2VvbV9jb2wobWFwcGluZyA9IGFlcyh4ID0gZmN0X3Jlb3JkZXIoYXMuZmFjdG9yKHdldGxhbmRJRCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2dFUiwgLmRlc2MgPSBUUlVFKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IC1sb2dFUiwgZmlsbCA9IC1sb2dFUikpICsgIyB0aGUgZ2VvbV9iYXIgZXF1aXZhbGVudCB3aGVuIGl0J3Mgbm90IGNvdW50aW5nIGNhc2VzLg0KICBnZW9tX2xpbmVyYW5nZShtYXBwaW5nID0gYWVzKHggPSBmY3RfcmVvcmRlcihhcy5mYWN0b3Iod2V0bGFuZElEKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ0VSLCAuZGVzYyA9IFRSVUUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHltaW4gPSAtbG93ZXJfbG9nRVIsIHltYXggPSAtdXBwZXJfbG9nRVIpLA0KICAgICAgICAgICAgICAgICBjb2xvciA9ICdncmV5NTAnLCBhbHBoYSA9IDAuMikgKw0KICAjIHNjYWxlX2ZpbGxfc3RlcHNuKGNvbG9ycyA9IGVycGFsX2xvZywNCiAgIyAgICAgICAgICAgICAgICAgICBicmVha3MgPSBlcmJyZWFrc19sb2dbMjpsZW5ndGgoZXJicmVha3NfbG9nKV0sDQogICMgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYyhtaW4oZXJicmVha3NfbG9nKSwgbWF4KGVyYnJlYWtzX2xvZykpLA0KICAjICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGVybGFiZWxzX2xvZywNCiAgIyAgICAgICAgICAgICAgICAgICBndWlkZSA9ICdsZWdlbmQnLA0KICAjICAgICAgICAgICAgICAgICAgIG5hbWUgPSBlcmxhYmVsKSArDQogDQogICMgc2NhbGVfZmlsbF9zdGVwc24oY29sb3JzID0gYyhyZXYoZXJwYWxfbG9nKSwgZ3BwcGFsX2xvZyksDQogICMgICAgICAgICAgICBicmVha3MgPSBjKC1yZXYoZXJicmVha3NfbG9nWzI6bGVuZ3RoKGVyYnJlYWtzX2xvZyldKSwgMCwNCiAgIyAgICAgICAgICAgICAgICAgICAgICAgZ3BwYnJlYWtzX2xvZ1syOmxlbmd0aChncHBicmVha3NfbG9nKV0pLA0KICAjICAgICAgICAgICAgbGltaXRzID0gYyhtaW4oLWVyYnJlYWtzX2xvZyksIG1heChncHBicmVha3NfbG9nKSksDQogICMgICAgICAgICAgICAjIGxhYmVscyA9IGMoZXJsYWJlbHNfbG9nLCBncHBsYWJlbHNfbG9nKSwNCiAgIyAgICAgICAgICAgIGd1aWRlID0gJ2xlZ2VuZCcsDQogICMgICAgICAgICAgICBuYW1lID0gJ2tnIE8yXG5hdCBtYXggZXh0ZW50JykgKw0KICBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHggPSBmY3RfcmVvcmRlcihhcy5mYWN0b3Iod2V0bGFuZElEKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ0dQUCwgLmRlc2MgPSBUUlVFKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGxvZ0dQUCwgZmlsbCA9IGxvZ0dQUCkpICsNCiAgZ2VvbV9saW5lcmFuZ2UobWFwcGluZyA9IGFlcyh4ID0gZmN0X3Jlb3JkZXIoYXMuZmFjdG9yKHdldGxhbmRJRCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2dHUFAsIC5kZXNjID0gVFJVRSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeW1pbiA9IGxvd2VyX2xvZ0dQUCwgeW1heCA9IHVwcGVyX2xvZ0dQUCksDQogICAgICAgICAgICAgICAgIGNvbG9yID0gJ2dyZXk1MCcsIGFscGhhID0gMC4yKSArDQogIGdlb21fbGluZShtYXBwaW5nID0gYWVzKHggPSBmY3RfcmVvcmRlcihhcy5mYWN0b3Iod2V0bGFuZElEKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nR1BQLCAuZGVzYyA9IFRSVUUpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBsb2dHUFAtbG9nRVIsIGdyb3VwID0gTkEpKSArDQogIA0KICAjIFRoaXMgcGFsZXR0ZSBpcyB2ZXJ5IHNsaWdodGx5IGRpZmZlcmVudCBiZWNhdXNlIGl0J3Mgbm90IGJpbm5lZCBhbmQgR1BQIGFib3ZlIHVzZXMgRW1lcmFsZCBpbnN0ZWFkIG9mIGdyZWVuLCBidXQgaXQncyBzdXJlIGNsb3NlIGFuZCB3YXkgZWFzaWVyDQogIHNjYWxlX2ZpbGxfY29udGludW91c19kaXZlcmdpbmcocGFsZXR0ZSA9ICJQdXJwbGUtR3JlZW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoLTQsIDQpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSAtNDo0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGJvdGhsYWJlbHNfbG9nKSArDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC00LCA0KSwNCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IC00OjQsDQogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBib3RobGFiZWxzX2xvZykgKw0KICAjIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKC1lcmJyZWFrc19sb2dbMjpsZW5ndGgoZXJicmVha3NfbG9nKV0sIA0KICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdwcGJyZWFrc19sb2dbMjpsZW5ndGgoZ3BwYnJlYWtzX2xvZyldKSwNCiAgIyAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygtcm91bmQoMTBeKGVyYnJlYWtzX2xvZ1syOmxlbmd0aChlcmJyZWFrc19sb2cpXSkpLCANCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZCgxMF4oZ3BwYnJlYWtzX2xvZ1syOmxlbmd0aChncHBicmVha3NfbG9nKV0pKSkpICsNCiAgbGFicyh4ID0gJ1dldGxhbmQgKG9yZGVyZWQgYnkgR1BQKScsIHkgPSAna2cgTzInLCBmaWxsID0gJ2tnIE8yJykgKw0KICAjIGFyZ2ggZG9lc24ndCB3b3JrIGJlY2F1c2UgaGFzIGJlZW4gcmVvcmRlcmVkDQogICMgc2NhbGVfeF9kaXNjcmV0ZShicmVha3MgPSBhcy5jaGFyYWN0ZXIoYygxLCAyNTAsIDUwMCwgNzUwLCAxMDAwKSkpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjc1LCAwLjU0KSkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuNzUsIDAuNTUpKQ0KYmFyYm90aA0KYGBgDQoNClRoZSBpbmFiaWxpdHkgdG8gbGFiZWwgeCBpbiBhIHJlYXNvbmFibGUgd2F5IGlzIGluZnVyaWF0aW5nLiBDYW4gSSBkbyBpdCBkaWZmZXJlbnRseT8gVHJ5IG1ha2luZyBhIGxhYmVsbGVkIGRhdGFzZXQgdGhhdCdzIGFycmFuZ2VkIGhvdyBJIHdhcyBkb2luZyBmY3RfcmVvcmRlci4NCg0KYGBge3J9DQpib3RoX3NmX0xNVV93aWRlMiA8LSBib3RoX3NmX0xNVV93aWRlICU+JSANCiAgYXJyYW5nZShkZXNjKGxvZ0dQUCkpICU+JSANCiAgbXV0YXRlKEdQUHJhbmsgPSByb3dfbnVtYmVyKCkpDQpgYGANCg0KTWFrZSB0aGUgcGxvdC4gSW50ZXJlc3RpbmcgdGhhdCB0aGUgbm90ZWJvb2sgcGxvdHRlciBkb2VzIGEgd29yc2Ugam9iIHRoYW4gdGhlIHBsb3RzIHBhbmVsIGZvciBhIHNjcmlwdC4gVGhlIGZ1bm55IGdhcHMgYXJlbid0IHJlYWxseSB0aGVyZS0gdG8gc2VlLCBydW4gYmFyYm90aCBpbiB0aGUgY29uc29sZS4NCg0KYGBge3J9DQpiYXJib3RoIDwtIGdncGxvdChib3RoX3NmX0xNVV93aWRlMikgKw0KICBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHggPSBHUFByYW5rLCANCiAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gLWxvZ0VSLCBmaWxsID0gLWxvZ0VSKSkgKyAjIHRoZSBnZW9tX2JhciBlcXVpdmFsZW50IHdoZW4gaXQncyBub3QgY291bnRpbmcgY2FzZXMuDQogIGdlb21fbGluZXJhbmdlKG1hcHBpbmcgPSBhZXMoeCA9IEdQUHJhbmssDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeW1pbiA9IC1sb3dlcl9sb2dFUiwgeW1heCA9IC11cHBlcl9sb2dFUiksDQogICAgICAgICAgICAgICAgIGNvbG9yID0gJ2dyZXk1MCcsIGFscGhhID0gMC4yKSArDQoNCmdlb21fY29sKG1hcHBpbmcgPSBhZXMoeCA9IEdQUHJhbmssIA0KICAgICAgICAgICAgICAgICAgICAgICB5ID0gbG9nR1BQLCBmaWxsID0gbG9nR1BQKSkgKw0KICBnZW9tX2xpbmVyYW5nZShtYXBwaW5nID0gYWVzKHggPSBHUFByYW5rLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHltaW4gPSBsb3dlcl9sb2dHUFAsIHltYXggPSB1cHBlcl9sb2dHUFApLA0KICAgICAgICAgICAgICAgICBjb2xvciA9ICdncmV5NTAnLCBhbHBoYSA9IDAuMikgKw0KICBnZW9tX2xpbmUobWFwcGluZyA9IGFlcyh4ID0gR1BQcmFuaywgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBsb2dHUFAtbG9nRVIsIGdyb3VwID0gTkEpKSArDQogIA0KICAjIFRoaXMgcGFsZXR0ZSBpcyB2ZXJ5IHNsaWdodGx5IGRpZmZlcmVudCBiZWNhdXNlIGl0J3Mgbm90IGJpbm5lZCBhbmQgR1BQIGFib3ZlIHVzZXMgRW1lcmFsZCBpbnN0ZWFkIG9mIGdyZWVuLCBidXQgaXQncyBzdXJlIGNsb3NlIGFuZCB3YXkgZWFzaWVyDQogIHNjYWxlX2ZpbGxfY29udGludW91c19kaXZlcmdpbmcocGFsZXR0ZSA9ICJQdXJwbGUtR3JlZW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoLTQsIDQpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSAtNDo0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGJvdGhsYWJlbHNfbG9nKSArDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC00LCA0KSwNCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IC00OjQsDQogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBib3RobGFiZWxzX2xvZykgKw0KICBsYWJzKHggPSAnV2V0bGFuZCAob3JkZXJlZCBieSBHUFApJywgeSA9ICdrZyBPMicsIGZpbGwgPSAna2cgTzInKSArDQogICMgc2NhbGVfeF9kaXNjcmV0ZShicmVha3MgPSBjKDEsIDI1MCwgNTAwLCA3NTAsIDEwMDApKSArDQogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjc1LCAwLjUzKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpDQpiYXJib3RoDQpgYGANCg0KU2F2ZSB0aGF0IGFzIGEgZmlndXJlDQoNCmBgYHtyfQ0KcGRmKGZpbGUucGF0aChzY3JpcHRPdXQsICdXZXJhaV9taXJyb3JncmFtLnBkZicpLCANCiAgICBvbmVmaWxlID0gRkFMU0UsIGhlaWdodCA9IDgvMi41NCwgd2lkdGggPSAxMi8yLjU0LCB1c2VEaW5nYmF0cyA9IEZBTFNFKQ0KcHJpbnQoYmFyYm90aCkNCmRldi5vZmYoKQ0KDQpwbmcoZmlsZS5wYXRoKHNjcmlwdE91dCwgJ1dlcmFpX21pcnJvcmdyYW0ucG5nJyksIA0KICAgIGhlaWdodCA9IDgvMi41NCwgd2lkdGggPSAxMi8yLjU0LCB1bml0cyA9ICdpbicsIHJlcyA9IDMwMCkNCnByaW50KGJhcmJvdGgpDQpkZXYub2ZmKCkNCmBgYA0KDQoNCldoYXQgYWJvdXQgRGFycmVuJ3MgZmluZ2VycHJpbnRzPyBUaGV5IHdvdWxkIG1heWJlIHRha2UgdGhlIEdQUCBhbmQgRVIgYW5kDQptYWtlIGEgZGVuc2l0eSB3aXRoIHRoZW0/IE9VdCBvZiB3aGF0PyBUaGUgd2V0bGFuZHM/IENvdWxkIHdvcmsuIFdvdWxkIG5lZWQgdG8NCnNvcnQgb3V0IHRoZSB3ZWlnaHRpbmcgYnkgdm9sdW1lLiBpdCdzIGFscmVhZHkgaW4gdGhlcmUsIGJ1dCB0aGF0IG1lYW5zIGl0DQp3aWxsIGdpdmUgZWFjaCBsb2NhdGlvbiB0aGUgc2FtZSB3ZWlnaHQuIEkgdGhpbmsgYSBiZXR0ZXIgdGhpbmcgZm9yIHRoZQ0KZmluZ2VycHJpbnRzIHdvdWxkIGJlIHRvIHJlbW92ZSBpdCwgYW5kIHRoZW4gZ2l2ZSBlYWNoIHdldGxhbmQgaXRzIHByZWRpY3RlZA0KcGVyIGxpdGVyIHJhdGUgYW5kIHdlaWdodCBieSB2b2x1bWUgSUUgdXNlIHRoZSBzdHJhaWdodCBwcmVkaWN0aW9ucyBhbmQgdGhlbg0Kd2VpZ2h0IGJ5IGludW5kYXRvbiBTaG91bGQgYmUgc3RyYWlnaHRmb3J3YXJkLCBidXQgbGlrZWx5IHdvbid0IGJlIA0KDQpXb3VsZCBiZSBhIHJlYWxseSBjb29sIHdheSB0byBsb29rIGF0IHNjZW5hcmlvcy4gZXNwZWNpYWxseSBpZiBJIGNhbiBkbyBpdA0KZm9yIHdob2xlIGNhdGNobWVudHMNCg0KQlVULCBiZWNhdXNlIEdQUCBhbmQgRVIgYXJlIGJvdGggbGluZWFyIGZpdHMgb2YgdGVtcCwgdGhleSB3aWxsIGV4YWN0bHkNCnByZWRpY3QgZWFjaCBvdGhlciBhY2NvcmRpbmcgdG8gYSBsaW5lYXIgcmVsYXRpb25zaGlwLCBhbmQgc28gdGhlcmUgd29uJ3QgYmUNCmFueSAyLWQgdmFyaWF0aW9uIGFuZCBhbGwgdGhlIHBvaW50cyB3aWxsIGZhbGwgb24gYSBsaW5lLiBJRSBmb3IgYSBnaXZlbiBHUFANCnRoZXJlIHdpbGwgb25seSBiZSBvbmUgRVIsIGFuZCBzbyB0aGUgZmluZ2VycHJpbnQgd2lsbCBiZSBhIGxpbmUuIENvb2wgaWRlYSwNCmJ1dCB3ZSdkIG5lZWQgbW9yZSBpbmZvIGFib3V0IHRoZWlyIHZhcmlhbmNlIHJlbGF0aXZlIHRvIGVhY2ggb3RoZXINCg0KIyMgQW5udWFsIHJlcG9ydGluZw0KDQpXaGF0IGFib3V0IG1ha2luZyBzb21lIGV4YW1wbGVzIHRvIGlsbHVzdHJhdGUgYW5udWFsIChvciBhcmJpdHJhcnkgdGltZSBwZXJpb2QpIHJlcG9ydGluZz8NCg0KRmlyc3QsIGVzdGFibGlzaCB0aGUgdGltZSBwZXJpb2RzIGFuZCBkbyB0aGUgYWdncmVnYXRpb25zIGFuZCBvdGhlciBkYXRhIG9yZ2FuaXNhdGlvbg0KDQpgYGB7cn0NCmludGVyRGF0ZXMgPC0gYXMuUE9TSVhjdChjKCIyMDE0LTA2LTMwIiwgIjIwMTUtMDYtMzAiLCAiMjAxNi0wNi0zMCIsICIyMDE3LTA2LTMwIiwgIjIwMTgtMDYtMzAiLCAiMjAxOS0wNi0zMCIpKQ0KDQp0ZW1wYW5udWFsIDwtIHRlbXBhZ2dyZWdhdGUod2VyYWlDcm9wVGVtcCwgYnlfdCA9IGFzLkRhdGUoaW50ZXJEYXRlcyksIEZVTiA9IG1lYW4sIG5hLnJtID0gVFJVRSkNCmludW5hbm51YWwgPC0gdGVtcGFnZ3JlZ2F0ZSh3ZXJhaUNyb3BJbnVuLCBieV90ID0gYXMuRGF0ZShpbnRlckRhdGVzKSwgRlVOID0gc3VtLCBuYS5ybSA9IFRSVUUpDQpncHBhbm51YWwgPC0gdGVtcGFnZ3JlZ2F0ZSh3ZXJhaUNyb3BQcmVkR1BQWzEsLF0sIGJ5X3QgPSBhcy5EYXRlKGludGVyRGF0ZXMpLCBGVU4gPSBzdW0sIG5hLnJtID0gVFJVRSkNCmVyYW5udWFsIDwtIHRlbXBhZ2dyZWdhdGUod2VyYWlDcm9wUHJlZEVSWzEsLF0sIGJ5X3QgPSBhcy5EYXRlKGludGVyRGF0ZXMpLCBGVU4gPSBzdW0sIG5hLnJtID0gVFJVRSkNCg0KIyBEYXRhIG9yZ2FuaXNhdGlvbg0KIyBBSEhIIHRoZSBmbG9wcGVkIGRpbXMNCmlmIChhdHRyaWJ1dGVzKHN0X2RpbWVuc2lvbnMoZ3BwYW5udWFsKSkkbmFtZVsxXSAhPSAnZ2VvbWV0cnknKSB7DQogIGdwcGFubnVhbCA8LSBhcGVybShncHBhbm51YWwsIGMoMiwxKSkNCn0NCmlmIChhdHRyaWJ1dGVzKHN0X2RpbWVuc2lvbnMoZXJhbm51YWwpKSRuYW1lWzFdICE9ICdnZW9tZXRyeScpIHsNCiAgZXJhbm51YWwgPC0gYXBlcm0oZXJhbm51YWwsIGMoMiwxKSkNCn0NCg0KIyBHUFANCmdwcFllYXJfc2YgPC0gZ3BwYW5udWFsICU+JSANCiAgc3RfYXNfc2YoKSAlPiUgDQogIHBpdm90X2xvbmdlcihjb2xzID0gLWdlb21ldHJ5LCBuYW1lc190byA9ICdXYXRlclllYXInLCB2YWx1ZXNfdG8gPSAnR1BQJykgJT4lDQogIG11dGF0ZShsb2dHUFAgPSAgbG9nMTAoMStHUFApKSAlPiUNCiAgc3RfYXNfc2YoKSAjIFJFQUxMWT8/Pw0KZ3BwWWVhcl9zZg0KDQptYXgoZ3BwWWVhcl9zZiRsb2dHUFApICMgaXMgd2l0aGluIA0KZ3BwQ29udHJvbCRncHBicmVha3MNCg0KIyBFUg0KZXJZZWFyX3NmIDwtIGVyYW5udWFsICU+JSANCiAgc3RfYXNfc2YoKSAlPiUgDQogIHBpdm90X2xvbmdlcihjb2xzID0gLWdlb21ldHJ5LCBuYW1lc190byA9ICdXYXRlclllYXInLCB2YWx1ZXNfdG8gPSAnRVInKSAlPiUNCiAgbXV0YXRlKGxvZ0VSID0gIGxvZzEwKDErRVIpKSAlPiUNCiAgc3RfYXNfc2YoKSAjIFJFQUxMWT8/Pw0KZXJZZWFyX3NmDQoNCiMgdGhlIHJhbmdlIHNob3VsZCBzdGlsbCB3b3JrIGZvciB0dGhlIGNvbG9ycw0KbWF4KGVyWWVhcl9zZiRsb2dFUikgIyBpcyB3aXRoaW4gDQplckNvbnRyb2wkZXJicmVha3MNCmBgYA0KDQpJIGRvbid0IHRoaW5rIEkgdXNlIHRoaXMsIGJ1dCBJIGRpZCBtYWtlIGEgZnVsbC13ZXJhaSBhZ2dyZWdhdGlvbg0KYGBge3J9DQojIGFnZ3JlZ2F0ZSB0byB3ZXJhaQ0KIyB0ZW1wIGFyZWEtd2VpZ2h0ZWQNCmFyZWFzIDwtIHRlbXBhbm51YWwgJT4lDQogIHN0X2dlb21ldHJ5KCkgJT4lDQogIHN0X2FyZWEoKSAlPiUNCiAgYXMubnVtZXJpYygpDQp0ZW1wVyA8LSBjYXRjaEFnZyA8LSBjYXRjaEFnZ1coc3RyaWN0ID0gdGVtcGFubnVhbCwgc3RyaWN0V2VpZ2h0cyA9IGFyZWFzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZVTiA9IG1lYW4sIHN1bW1hcnlQb2x5ID0gcmFtc2FyVzEpDQogIA0KaW51blcgPC0gYWdncmVnYXRlKGludW5hbm51YWwsIA0KICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gcmFtc2FyVzEsIA0KICAgICAgICAgICAgICAgICAgICAgIEZVTiA9IHN1bSwgbmEucm0gPSBUUlVFKQ0KDQpncHBXIDwtIGFnZ3JlZ2F0ZShncHBhbm51YWwsIA0KICAgICAgICAgICAgICAgICAgIGJ5ID0gcmFtc2FyVzEsIA0KICAgICAgICAgICAgICAgICAgIEZVTiA9IHN1bSwgbmEucm0gPSBUUlVFKQ0KZXJXIDwtIGFnZ3JlZ2F0ZShlcmFubnVhbCwgDQogICAgICAgICAgICAgICAgICAgYnkgPSByYW1zYXJXMSwgDQogICAgICAgICAgICAgICAgICAgRlVOID0gc3VtLCBuYS5ybSA9IFRSVUUpDQoNCmBgYA0KDQojIyMgVG1hcCBQbG90cw0KTWFrZSBhIGZhY2V0dGVkIHRtYXANCg0KQWdhaW4sIGNhbiBJIGRvIHRoaXMgYnkgbW9kaWZ5aW5nIHdoYXQgSSBwYXNzIHRvIHRoZSBwbG90dGluZyBmdW5jdGlvbnM/IEFsbW9zdCBjZXJ0YWlubHkNCg0KVGhlIHVuaXRzIGFyZSB3ZWlyZCwgYmVjYXVzZSB0aGlzIGlzIGFkZGVkIHVwIGFjcm9zcyBiaW1vbnRocy4gSXQncyBuZWl0aGVyIHRoZSB0b3RhbCBvciB0aGUgYXZlcmFnZS5Ob3QgcmVhbGx5IHN1cmUgd2hhdCB0byBjYWxsIGl0DQoNCkNoYW5nZSB0aGUgdG1hcF9tb2RlKCkgZGVwZW5kaW5nIG9uIHRoZSBwdXJwb3NlLiBETyBOT1QgQ0hBTkdFIFRPIFZJRVcgQU5EIFRSWSBUTyBSVU4gSEVSRS4gSUYgWU9VIFdBTlQgVE8gVklFVywgQ0hBTkdFIEFORCBSVU4gSU4gQ09OU09MRS4gVGhlIG1hcmtkb3duIHdpbGwgcnVuIGl0LCBidXQgaW5jcmVkaWJseSBzbG93bHkuDQoNCkdQUA0KDQpgYGB7cn0NCnRtYXBfbW9kZSgncGxvdCcpDQoNCmdwcHlybGFiZWwgPC0gJ1RvdGFsIFllYXJseSBHUFAgKGtnIDAyKVxuYXQgbWF4IGJpbW9udGggZXh0ZW50cycNCg0KICBncHBBbm51YWxfdG0gPC0gZ3BwWWVhcl9zZiAlPiUNCiAgICBmaWx0ZXIoV2F0ZXJZZWFyICE9ICcyMDE0LTA2LTI5JykgJT4lICMgYmVjYXVzZSBhIDUtcGFuZWwgaXMgdWdseQ0KICAgIHRtX3NoYXBlKCkgKw0KICAgIHRtX2ZpbGwoY29sID0gJ2xvZ0dQUCcsDQogICAgICAgICAgICBwYWxldHRlID0gZ3BwQ29udHJvbCRncHBwYWwsDQogICAgICAgICAgICBicmVha3MgPSBncHBDb250cm9sJGdwcGJyZWFrcywNCiAgICAgICAgICAgIGxhYmVscyA9IGdwcENvbnRyb2wkZ3BwbGFiZWxzLA0KICAgICAgICAgICAgdGl0bGUgPSBncHB5cmxhYmVsKSArIA0KICAgIHRtX2ZhY2V0cyhieSA9ICdXYXRlclllYXInKQ0KICBncHBBbm51YWxfdG0NCmBgYA0KDQpFUg0KDQpgYGB7cn0NCmVyeXJsYWJlbCA8LSAnVG90YWwgWWVhcmx5IEVSIChrZyAwMilcbmF0IG1heCBiaW1vbnRoIGV4dGVudHMnDQogIA0KICBlckFubnVhbF90bSA8LSBlclllYXJfc2YgJT4lDQogICAgZmlsdGVyKFdhdGVyWWVhciAhPSAnMjAxNC0wNi0yOScpICU+JSAjIGJlY2F1c2UgYSA1LXBhbmVsIGlzIHVnbHkNCiAgICB0bV9zaGFwZSgpICsNCiAgICB0bV9maWxsKGNvbCA9ICdsb2dFUicsDQogICAgICAgICAgICBwYWxldHRlID0gZXJDb250cm9sJGVycGFsLA0KICAgICAgICAgICAgYnJlYWtzID0gZXJDb250cm9sJGVyYnJlYWtzLA0KICAgICAgICAgICAgbGFiZWxzID0gZXJDb250cm9sJGVybGFiZWxzLA0KICAgICAgICAgICAgdGl0bGUgPSBlcnlybGFiZWwpICsgDQogICAgdG1fZmFjZXRzKGJ5ID0gJ1dhdGVyWWVhcicpDQogIGVyQW5udWFsX3RtDQpgYGANCg0KDQojIyMgR0dQTE9UDQoNCmdwcA0KDQpgYGB7cn0NCmdwcEFubnVhbF9nZyA8LSBncHBZZWFyX3NmICU+JQ0KICAgIGZpbHRlcihXYXRlclllYXIgIT0gJzIwMTQtMDYtMjknKSAlPiUgIyBiZWNhdXNlIGEgNS1wYW5lbCBpcyB1Z2x5DQogICAgZ2dwbG90KCkgKw0KICAgIGdlb21fc2YobWFwcGluZyA9IGFlcyhmaWxsID0gbG9nR1BQKSwgY29sb3IgPSBOQSkgKw0KICAgIGNvb3JkX3NmKCkgKw0KICAgICMgQ2xvc2VzdCB0byB0aGUgdG1hcA0KICAgIHNjYWxlX2ZpbGxfc3RlcHNuKGNvbG9ycyA9IGdwcENvbnRyb2wkZ3BwcGFsLA0KICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGdwcENvbnRyb2wkZ3BwYnJlYWtzWzI6bGVuZ3RoKGdwcENvbnRyb2wkZ3BwYnJlYWtzKV0sDQogICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYyhtaW4oZ3BwQ29udHJvbCRncHBicmVha3MpLCBtYXgoZ3BwQ29udHJvbCRncHBicmVha3MpKSwNCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBncHBDb250cm9sJGdwcGxhYmVscywNCiAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9ICdsZWdlbmQnLA0KICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSBncHB5cmxhYmVsKSArDQogICAgZmFjZXRfd3JhcCh2YXJzKFdhdGVyWWVhcikpDQogIGdwcEFubnVhbF9nZw0KYGBgDQoNCkVSDQpgYGB7cn0NCmVyQW5udWFsX2dnIDwtIGVyWWVhcl9zZiAlPiUNCiAgICBmaWx0ZXIoV2F0ZXJZZWFyICE9ICcyMDE0LTA2LTI5JykgJT4lICMgYmVjYXVzZSBhIDUtcGFuZWwgaXMgdWdseQ0KICAgIGdncGxvdCgpICsNCiAgICBnZW9tX3NmKG1hcHBpbmcgPSBhZXMoZmlsbCA9IGxvZ0VSKSwgY29sb3IgPSBOQSkgKw0KICAgIGNvb3JkX3NmKCkgKw0KICAgICMgQ2xvc2VzdCB0byB0aGUgdG1hcA0KICAgIHNjYWxlX2ZpbGxfc3RlcHNuKGNvbG9ycyA9IGVyQ29udHJvbCRlcnBhbCwNCiAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBlckNvbnRyb2wkZXJicmVha3NbMjpsZW5ndGgoZXJDb250cm9sJGVyYnJlYWtzKV0sDQogICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYyhtaW4oZXJDb250cm9sJGVyYnJlYWtzKSwgbWF4KGVyQ29udHJvbCRlcmJyZWFrcykpLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGVyQ29udHJvbCRlcmxhYmVscywNCiAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9ICdsZWdlbmQnLA0KICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSBlcnlybGFiZWwpICsNCiAgICBmYWNldF93cmFwKHZhcnMoV2F0ZXJZZWFyKSkNCiAgZXJBbm51YWxfZ2cNCmBgYA0KDQpTdGFjayAzIHllYXJzIG9mIGdwcCBhbmQgRVIgdG9nZXRoZXIgZm9yIHRoZSBkb2N1bWVudA0KDQpgYGB7cn0NCmdwcEFubnVhbF9nZzMgPC0gZ3BwWWVhcl9zZiAlPiUNCiAgICBmaWx0ZXIoV2F0ZXJZZWFyICE9ICcyMDE0LTA2LTI5JyAmIFdhdGVyWWVhciAhPSAnMjAxOC0wNi0yOScpICU+JSAjIGJlY2F1c2UgYSA1LXBhbmVsIGlzIHVnbHkNCiAgICBnZ3Bsb3QoKSArDQogICAgZ2VvbV9zZihtYXBwaW5nID0gYWVzKGZpbGwgPSBsb2dHUFApLCBjb2xvciA9IE5BKSArDQogICAgY29vcmRfc2YoKSArDQogICAgIyBDbG9zZXN0IHRvIHRoZSB0bWFwDQogICAgc2NhbGVfZmlsbF9zdGVwc24oY29sb3JzID0gZ3BwQ29udHJvbCRncHBwYWwsDQogICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gZ3BwQ29udHJvbCRncHBicmVha3NbMjpsZW5ndGgoZ3BwQ29udHJvbCRncHBicmVha3MpXSwNCiAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKG1pbihncHBDb250cm9sJGdwcGJyZWFrcyksIG1heChncHBDb250cm9sJGdwcGJyZWFrcykpLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGdwcENvbnRyb2wkZ3BwbGFiZWxzLA0KICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gJ2xlZ2VuZCcsDQogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9IGdwcHlybGFiZWwpICsNCiAgICBmYWNldF9ncmlkKFdhdGVyWWVhcn4uKSArIA0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nKQ0KICAjIGdwcEFubnVhbF9nZzMgDQogIA0KICBlckFubnVhbF9nZzMgPC0gZXJZZWFyX3NmICU+JQ0KICAgIGZpbHRlcihXYXRlclllYXIgIT0gJzIwMTQtMDYtMjknICYgV2F0ZXJZZWFyICE9ICcyMDE4LTA2LTI5JykgJT4lICMgYmVjYXVzZSBhIDUtcGFuZWwgaXMgdWdseQ0KICAgIGdncGxvdCgpICsNCiAgICBnZW9tX3NmKG1hcHBpbmcgPSBhZXMoZmlsbCA9IGxvZ0VSKSwgY29sb3IgPSBOQSkgKw0KICAgIGNvb3JkX3NmKCkgKw0KICAgICMgQ2xvc2VzdCB0byB0aGUgdG1hcA0KICAgIHNjYWxlX2ZpbGxfc3RlcHNuKGNvbG9ycyA9IGVyQ29udHJvbCRlcnBhbCwNCiAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBlckNvbnRyb2wkZXJicmVha3NbMjpsZW5ndGgoZXJDb250cm9sJGVyYnJlYWtzKV0sDQogICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYyhtaW4oZXJDb250cm9sJGVyYnJlYWtzKSwgbWF4KGVyQ29udHJvbCRlcmJyZWFrcykpLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGVyQ29udHJvbCRlcmxhYmVscywNCiAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9ICdsZWdlbmQnLA0KICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSBlcnlybGFiZWwpICsNCiAgICBmYWNldF9ncmlkKFdhdGVyWWVhcn4uKSArIA0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nKQ0KICAjIGVyQW5udWFsX2dnMw0KDQogIGFubnVhbEdQUEVSIDwtIGdncHVicjo6Z2dhcnJhbmdlKGdwcEFubnVhbF9nZzMgKyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZCh0aXRsZS5wb3NpdGlvbiA9ICd0b3AnKSkgKw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoZW1lX2dyZXkoYmFzZV9zaXplID0gOCkgKyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjMsICdjbScpKSwgDQogICAgICAgICAgICAgICAgICAgIGVyQW5udWFsX2dnMyArIA0KICAgICAgICAgICAgICAgICAgICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlLnBvc2l0aW9uID0gJ3RvcCcpKSArDQogICAgICAgICAgICAgICAgICAgICAgdGhlbWVfZ3JleShiYXNlX3NpemUgPSA4KSArIA0KICAgICAgICAgICAgICAgICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuMywgJ2NtJykpLA0KICAgICAgICAgICAgICAgICAgICBuY29sID0gMiwgbnJvdyA9IDEpDQogIGFubnVhbEdQUEVSDQpgYGANCg0KUHJpbnQgb3V0IHRoZSBnZ3Bsb3RzIGZvciB0aGUgZG9jDQoNCmBgYHtyfQ0KICMgSnVzdCBwcmludCB0aGUgZ2dwbG90cw0KICAjIENhbiBJIHByaW50IHRob3NlPw0KICBwZGYoZmlsZS5wYXRoKHNjcmlwdE91dCwgJ1dlcmFpX2dnX0FubnVhbC5wZGYnKSwgDQogICAgICBvbmVmaWxlID0gRkFMU0UsIGhlaWdodCA9IDEyLzIuNTQsIHdpZHRoID0gMTYvMi41NCwgdXNlRGluZ2JhdHMgPSBGQUxTRSkNCiAgcHJpbnQoYW5udWFsR1BQRVIpDQogIGRldi5vZmYoKQ0KICANCiAgcG5nKGZpbGUucGF0aChzY3JpcHRPdXQsICdXZXJhaV9nZ19Bbm51YWwucG5nJyksIA0KICAgICAgaGVpZ2h0ID0gMTIvMi41NCwgd2lkdGggPSAxNi8yLjU0LCB1bml0cyA9ICdpbicsIHJlcyA9IDMwMCkNCiAgcHJpbnQoYW5udWFsR1BQRVIpDQogIGRldi5vZmYoKQ0KICANCg0KYGBgDQoNCg==